Audacity  2.2.2
EffectManager.cpp
Go to the documentation of this file.
1 /**********************************************************************
2 
3  Audacity: A Digital Audio Editor
4 
5  EffectManager.cpp
6 
7  Audacity(R) is copyright (c) 1999-2008 Audacity Team.
8  License: GPL v2. See License.txt.
9 
10 **********************************************************************/
11 
12 #include "../Audacity.h"
13 
14 #include <algorithm>
15 #include <wx/stopwatch.h>
16 #include <wx/tokenzr.h>
17 
18 #include "../Experimental.h"
19 #include "../widgets/ErrorDialog.h"
20 
21 #if defined(EXPERIMENTAL_EFFECTS_RACK)
22 #include "EffectRack.h"
23 #endif
24 
25 #include "EffectManager.h"
26 
27 // ============================================================================
28 //
29 // Create singleton and return reference
30 //
31 // (Thread-safe...no active threading during construction or after destruction)
32 // ============================================================================
34 {
35  static EffectManager em;
36  return em;
37 }
38 
40 {
41  mRealtimeLock.Enter();
42  mRealtimeActive = false;
43  mRealtimeSuspended = true;
44  mRealtimeLatency = 0;
45  mRealtimeLock.Leave();
46  mSkipStateFlag = false;
47 
48 #if defined(EXPERIMENTAL_EFFECTS_RACK)
49  mRack = NULL;
50 #endif
51 }
52 
54 {
55 #if defined(EXPERIMENTAL_EFFECTS_RACK)
56  // wxWidgets has already destroyed the rack since it was derived from wxFrame. So
57  // no need to DELETE it here.
58 #endif
59 }
60 
61 // Here solely for the purpose of Nyquist Workbench until
62 // a better solution is devised.
64 {
65  const PluginID & ID = PluginManager::Get().RegisterPlugin(f);
66 
67  mEffects[ID] = f;
68 
69  return ID;
70 }
71 
72 // Here solely for the purpose of Nyquist Workbench until
73 // a better solution is devised.
74 void EffectManager::UnregisterEffect(const PluginID & ID)
75 {
76  PluginID id = ID;
78  mEffects.erase(id);
79 }
80 
81 bool EffectManager::DoEffect(const PluginID & ID,
82  wxWindow *parent,
83  double projectRate,
84  TrackList *list,
85  TrackFactory *factory,
86  SelectedRegion *selectedRegion,
87  bool shouldPrompt /* = true */)
88 
89 {
90  this->SetSkipStateFlag(false);
91  Effect *effect = GetEffect(ID);
92 
93  if (!effect)
94  {
95  return false;
96  }
97 
98 #if defined(EXPERIMENTAL_EFFECTS_RACK)
99  if (effect->SupportsRealtime())
100  {
101  GetRack()->Add(effect);
102  }
103 #endif
104 
105  bool res = effect->DoEffect(parent,
106  projectRate,
107  list,
108  factory,
109  selectedRegion,
110  shouldPrompt);
111 
112  return res;
113 }
114 
115 wxString EffectManager::GetEffectName(const PluginID & ID)
116 {
117  return PluginManager::Get().GetName(ID);
118 }
119 
120 wxString EffectManager::GetEffectFamilyName(const PluginID & ID)
121 {
122  auto effect = GetEffect(ID);
123  if (effect)
124  return effect->GetFamilyName();
125  return {};
126 }
127 
128 wxString EffectManager::GetEffectIdentifier(const PluginID & ID)
129 {
130  wxString name = (PluginManager::Get().GetSymbol(ID));
131 
132  // Get rid of leading and trailing white space
133  name.Trim(true).Trim(false);
134 
135  if (name == wxEmptyString)
136  {
137  return name;
138  }
139 
140  wxStringTokenizer st(name, wxT(" "));
141  wxString id;
142 
143  // CamelCase the name
144  while (st.HasMoreTokens())
145  {
146  wxString tok = st.GetNextToken();
147 
148  id += tok.Left(1).MakeUpper() + tok.Mid(1).MakeLower();
149  }
150 
151  return id;
152 }
153 
154 wxString EffectManager::GetEffectDescription(const PluginID & ID)
155 {
156  Effect *effect = GetEffect(ID);
157 
158  if (effect)
159  {
160  return wxString::Format(_("Applied effect: %s"), GetEffectName(ID));
161  }
162 
163  return wxEmptyString;
164 }
165 
166 bool EffectManager::IsHidden(const PluginID & ID)
167 {
168  Effect *effect = GetEffect(ID);
169 
170  if (effect)
171  {
172  return effect->IsHidden();
173  }
174 
175  return false;
176 }
177 
179 {
180  mSkipStateFlag = flag;
181 }
182 
184 {
185  return mSkipStateFlag;
186 }
187 
188 bool EffectManager::SupportsAutomation(const PluginID & ID)
189 {
190  const PluginDescriptor *plug = PluginManager::Get().GetPlugin(ID);
191  if (plug)
192  {
193  return plug->IsEffectAutomatable();
194  }
195 
196  return false;
197 }
198 
199 wxString EffectManager::GetEffectParameters(const PluginID & ID)
200 {
201  Effect *effect = GetEffect(ID);
202 
203  if (effect)
204  {
205  wxString parms;
206 
207  effect->GetAutomationParameters(parms);
208 
209  // Some effects don't have automatable parameters and will not return
210  // anything, so try to get the active preset (current or factory).
211  if (parms.IsEmpty())
212  {
213  parms = GetDefaultPreset(ID);
214  }
215 
216  return parms;
217  }
218 
219  return wxEmptyString;
220 }
221 
222 bool EffectManager::SetEffectParameters(const PluginID & ID, const wxString & params)
223 {
224  Effect *effect = GetEffect(ID);
225 
226  if (!effect)
227  {
228  return false;
229  }
230 
231  EffectAutomationParameters eap(params);
232 
233  if (eap.HasEntry(wxT("Use Preset")))
234  {
235  return effect->SetAutomationParameters(eap.Read(wxT("Use Preset")));
236  }
237 
238  return effect->SetAutomationParameters(params);
239 }
240 
241 bool EffectManager::PromptUser(const PluginID & ID, wxWindow *parent)
242 {
243  Effect *effect = GetEffect(ID);
244  bool result = false;
245 
246  if (effect)
247  {
248  result = effect->PromptUser(parent);
249  }
250 
251  return result;
252 }
253 
254 bool EffectManager::HasPresets(const PluginID & ID)
255 {
256  Effect *effect = GetEffect(ID);
257 
258  if (!effect)
259  {
260  return false;
261  }
262 
263  return effect->GetUserPresets().GetCount() > 0 ||
264  effect->GetFactoryPresets().GetCount() > 0 ||
265  effect->HasCurrentSettings() ||
266  effect->HasFactoryDefaults();
267 }
268 
269 wxString EffectManager::GetPreset(const PluginID & ID, const wxString & params, wxWindow * parent)
270 {
271  Effect *effect = GetEffect(ID);
272 
273  if (!effect)
274  {
275  return wxEmptyString;
276  }
277 
278  EffectAutomationParameters eap(params);
279 
280  wxString preset;
281  if (eap.HasEntry(wxT("Use Preset")))
282  {
283  preset = eap.Read(wxT("Use Preset"));
284  }
285 
286  preset = effect->GetPreset(parent, preset);
287  if (preset.IsEmpty())
288  {
289  return preset;
290  }
291 
292  eap.DeleteAll();
293 
294  eap.Write(wxT("Use Preset"), preset);
295  eap.GetParameters(preset);
296 
297  return preset;
298 }
299 
300 wxString EffectManager::GetDefaultPreset(const PluginID & ID)
301 {
302  Effect *effect = GetEffect(ID);
303 
304  if (!effect)
305  {
306  return wxEmptyString;
307  }
308 
309  wxString preset;
310  if (effect->HasCurrentSettings())
311  {
313  }
314  else if (effect->HasFactoryDefaults())
315  {
317  }
318 
319  if (!preset.IsEmpty())
320  {
321  EffectAutomationParameters eap;
322 
323  eap.Write(wxT("Use Preset"), preset);
324  eap.GetParameters(preset);
325  }
326 
327  return preset;
328 }
329 
330 void EffectManager::SetBatchProcessing(const PluginID & ID, bool start)
331 {
332  Effect *effect = GetEffect(ID);
333 
334  if (!effect)
335  {
336  return;
337  }
338 
339  effect->SetBatchProcessing(start);
340 }
341 
342 #if defined(EXPERIMENTAL_EFFECTS_RACK)
343 EffectRack *EffectManager::GetRack()
344 {
345  if (!mRack)
346  {
347  // EffectRack is constructed with the current project as owner, so safenew is OK
348  mRack = safenew EffectRack();
349  // Make sure what I just commented remains true:
350  wxASSERT(mRack->GetParent());
351  mRack->CenterOnParent();
352  }
353 
354  return mRack;
355 }
356 
357 void EffectManager::ShowRack()
358 {
359  GetRack()->Show(!GetRack()->IsShown());
360 }
361 
363 {
364  // Block RealtimeProcess()
365  RealtimeSuspend();
366 
367  // Tell any effects no longer in the chain to clean up
368  for (auto e: mRealtimeEffects)
369  {
370  // Scan the NEW chain for the effect
371  for (auto e1: effects)
372  {
373  // Found it so we're done
374  if (e == e1)
375  {
376  e = NULL;
377  break;
378  }
379  }
380 
381  // Must not have been in the NEW chain, so tell it to cleanup
382  if (e && mRealtimeActive)
383  {
384  e->RealtimeFinalize();
385  }
386  }
387 
388  // Tell any NEW effects to get ready
389  for (auto e : effects)
390  {
391  // Scan the old chain for the effect
392  for (auto e1 : mRealtimeEffects)
393  {
394  // Found it so tell effect to get ready
395  if (e == e1)
396  {
397  e = NULL;
398  break;
399  }
400  }
401 
402  // Must not have been in the old chain, so tell it to initialize
403  if (e && mRealtimeActive)
404  {
405  e->RealtimeInitialize();
406  }
407  }
408 
409  // Get rid of the old chain
410  // And install the NEW one
411  mRealtimeEffects = effects;
412 
413  // Allow RealtimeProcess() to, well, process
414  RealtimeResume();
415 }
416 #endif
417 
419 {
420  return mRealtimeEffects.size() != 0;
421 }
422 
424 {
425  return mRealtimeSuspended;
426 }
427 
429 {
430  // Block RealtimeProcess()
431  RealtimeSuspend();
432 
433  // Initialize effect if realtime is already active
434  if (mRealtimeActive)
435  {
436  // Initialize realtime processing
437  effect->RealtimeInitialize();
438 
439  // Add the required processors
440  for (size_t i = 0, cnt = mRealtimeChans.size(); i < cnt; i++)
441  {
443  }
444  }
445 
446  // Add to list of active effects
447  mRealtimeEffects.push_back(effect);
448 
449  // Allow RealtimeProcess() to, well, process
450  RealtimeResume();
451 }
452 
454 {
455  // Block RealtimeProcess()
456  RealtimeSuspend();
457 
458  if (mRealtimeActive)
459  {
460  // Cleanup realtime processing
461  effect->RealtimeFinalize();
462  }
463 
464  // Remove from list of active effects
465  auto end = mRealtimeEffects.end();
466  auto found = std::find(mRealtimeEffects.begin(), end, effect);
467  if (found != end)
468  mRealtimeEffects.erase(found);
469 
470  // Allow RealtimeProcess() to, well, process
471  RealtimeResume();
472 }
473 
475 {
476  // The audio thread should not be running yet, but protect anyway
477  RealtimeSuspend();
478 
479  // (Re)Set processor parameters
480  mRealtimeChans.clear();
481  mRealtimeRates.clear();
482 
483  // RealtimeAdd/RemoveEffect() needs to know when we're active so it can
484  // initialize newly added effects
485  mRealtimeActive = true;
486 
487  // Tell each effect to get ready for action
488  for (auto e : mRealtimeEffects) {
489  e->SetSampleRate(rate);
490  e->RealtimeInitialize();
491  }
492 
493  // Get things moving
494  RealtimeResume();
495 }
496 
497 void EffectManager::RealtimeAddProcessor(int group, unsigned chans, float rate)
498 {
499  for (auto e : mRealtimeEffects)
500  e->RealtimeAddProcessor(group, chans, rate);
501 
502  mRealtimeChans.push_back(chans);
503  mRealtimeRates.push_back(rate);
504 }
505 
507 {
508  // Make sure nothing is going on
509  RealtimeSuspend();
510 
511  // It is now safe to clean up
512  mRealtimeLatency = 0;
513 
514  // Tell each effect to clean up as well
515  for (auto e : mRealtimeEffects)
516  e->RealtimeFinalize();
517 
518  // Reset processor parameters
519  mRealtimeChans.clear();
520  mRealtimeRates.clear();
521 
522  // No longer active
523  mRealtimeActive = false;
524 }
525 
527 {
528  mRealtimeLock.Enter();
529 
530  // Already suspended...bail
531  if (mRealtimeSuspended)
532  {
533  mRealtimeLock.Leave();
534  return;
535  }
536 
537  // Show that we aren't going to be doing anything
538  mRealtimeSuspended = true;
539 
540  // And make sure the effects don't either
541  for (auto e : mRealtimeEffects)
542  e->RealtimeSuspend();
543 
544  mRealtimeLock.Leave();
545 }
546 
548 {
549  mRealtimeLock.Enter();
550 
551  // Already running...bail
552  if (!mRealtimeSuspended)
553  {
554  mRealtimeLock.Leave();
555  return;
556  }
557 
558  // Tell the effects to get ready for more action
559  for (auto e : mRealtimeEffects)
560  e->RealtimeResume();
561 
562  // And we should too
563  mRealtimeSuspended = false;
564 
565  mRealtimeLock.Leave();
566 }
567 
568 //
569 // This will be called in a different thread than the main GUI thread.
570 //
572 {
573  // Protect ourselves from the main thread
574  mRealtimeLock.Enter();
575 
576  // Can be suspended because of the audio stream being paused or because effects
577  // have been suspended.
578  if (!mRealtimeSuspended)
579  {
580  for (auto e : mRealtimeEffects)
581  {
582  if (e->IsRealtimeActive())
583  e->RealtimeProcessStart();
584  }
585  }
586 
587  mRealtimeLock.Leave();
588 }
589 
590 //
591 // This will be called in a different thread than the main GUI thread.
592 //
593 size_t EffectManager::RealtimeProcess(int group, unsigned chans, float **buffers, size_t numSamples)
594 {
595  // Protect ourselves from the main thread
596  mRealtimeLock.Enter();
597 
598  // Can be suspended because of the audio stream being paused or because effects
599  // have been suspended, so allow the samples to pass as-is.
600  if (mRealtimeSuspended || mRealtimeEffects.empty())
601  {
602  mRealtimeLock.Leave();
603  return numSamples;
604  }
605 
606  // Remember when we started so we can calculate the amount of latency we
607  // are introducing
608  wxMilliClock_t start = wxGetLocalTimeMillis();
609 
610  // Allocate the in/out buffer arrays
611  float **ibuf = (float **) alloca(chans * sizeof(float *));
612  float **obuf = (float **) alloca(chans * sizeof(float *));
613 
614  // And populate the input with the buffers we've been given while allocating
615  // NEW output buffers
616  for (unsigned int i = 0; i < chans; i++)
617  {
618  ibuf[i] = buffers[i];
619  obuf[i] = (float *) alloca(numSamples * sizeof(float));
620  }
621 
622  // Now call each effect in the chain while swapping buffer pointers to feed the
623  // output of one effect as the input to the next effect
624  size_t called = 0;
625  for (auto e : mRealtimeEffects)
626  {
627  if (e->IsRealtimeActive())
628  {
629  e->RealtimeProcess(group, chans, ibuf, obuf, numSamples);
630  called++;
631  }
632 
633  for (unsigned int j = 0; j < chans; j++)
634  {
635  float *temp;
636  temp = ibuf[j];
637  ibuf[j] = obuf[j];
638  obuf[j] = temp;
639  }
640  }
641 
642  // Once we're done, we might wind up with the last effect storing its results
643  // in the temporary buffers. If that's the case, we need to copy it over to
644  // the caller's buffers. This happens when the number of effects proccessed
645  // is odd.
646  if (called & 1)
647  {
648  for (unsigned int i = 0; i < chans; i++)
649  {
650  memcpy(buffers[i], ibuf[i], numSamples * sizeof(float));
651  }
652  }
653 
654  // Remember the latency
655  mRealtimeLatency = (int) (wxGetLocalTimeMillis() - start).GetValue();
656 
657  mRealtimeLock.Leave();
658 
659  //
660  // This is wrong...needs to handle tails
661  //
662  return numSamples;
663 }
664 
665 //
666 // This will be called in a different thread than the main GUI thread.
667 //
669 {
670  // Protect ourselves from the main thread
671  mRealtimeLock.Enter();
672 
673  // Can be suspended because of the audio stream being paused or because effects
674  // have been suspended.
675  if (!mRealtimeSuspended)
676  {
677  for (auto e : mRealtimeEffects)
678  {
679  if (e->IsRealtimeActive())
680  e->RealtimeProcessEnd();
681  }
682  }
683 
684  mRealtimeLock.Leave();
685 }
686 
688 {
689  return mRealtimeLatency;
690 }
691 
692 Effect *EffectManager::GetEffect(const PluginID & ID)
693 {
694  // Must have a "valid" ID
695  if (ID.IsEmpty())
696  {
697  return NULL;
698  }
699 
700  // TODO: This is temporary and should be redone when all effects are converted
701  if (mEffects.find(ID) == mEffects.end())
702  {
703  // This will instantiate the effect client if it hasn't already been done
704  EffectIdentInterface *ident = dynamic_cast<EffectIdentInterface *>(PluginManager::Get().GetInstance(ID));
705  if (ident && ident->IsLegacy())
706  {
707  auto effect = dynamic_cast<Effect *>(ident);
708  if (effect && effect->Startup(NULL))
709  {
710  mEffects[ID] = effect;
711  return effect;
712  }
713  }
714 
715  auto effect = std::make_shared<Effect>(); // TODO: use make_unique and store in std::unordered_map
716  if (effect)
717  {
718  EffectClientInterface *client = dynamic_cast<EffectClientInterface *>(ident);
719  if (client && effect->Startup(client))
720  {
721  auto pEffect = effect.get();
722  mEffects[ID] = pEffect;
723  mHostEffects[ID] = std::move(effect);
724  return pEffect;
725  }
726  }
727 
728  AudacityMessageBox(wxString::Format(_("Attempting to initialize the following effect failed:\n\n%s\n\nMore information may be available in Help->Show Log"),
729  PluginManager::Get().GetName(ID)),
730  _("Effect failed to initialize"));
731 
732  return NULL;
733  }
734 
735  return mEffects[ID];
736 }
737 
738 const PluginID & EffectManager::GetEffectByIdentifier(const wxString & strTarget)
739 {
740  static PluginID empty;
741  if (strTarget == wxEmptyString) // set GetEffectIdentifier to wxT("") to not show an effect in Batch mode
742  {
743  return empty;
744  }
745 
748  while (plug)
749  {
750  if (GetEffectIdentifier(plug->GetID()).IsSameAs(strTarget))
751  {
752  return plug->GetID();
753  }
754  plug = pm.GetNextPlugin(PluginTypeEffect);
755  }
756 
757  return empty;;
758 }
759 
A list of TrackListNode items.
Definition: Track.h:611
static CommandHandlerObject & ident(AudacityProject &project)
Definition: Menus.cpp:290
virtual void SetBatchProcessing(bool start)
Definition: Effect.cpp:1147
virtual ~EffectManager()
size_t RealtimeProcess(int group, unsigned chans, float **buffers, size_t numSamples)
bool DoEffect(const PluginID &ID, wxWindow *parent, double projectRate, TrackList *list, TrackFactory *factory, SelectedRegion *selectedRegion, bool shouldPrompt=true)
bool GetAutomationParameters(EffectAutomationParameters &parms) override
Definition: Effect.cpp:589
bool HasPresets(const PluginID &ID)
const PluginID & RegisterEffect(Effect *f)
EffectMap mEffects
void RealtimeAddProcessor(int group, unsigned chans, float rate)
wxString GetEffectFamilyName(const PluginID &ID)
wxString GetDefaultPreset(const PluginID &ID)
const PluginID & GetEffectByIdentifier(const wxString &strTarget)
bool DoEffect(wxWindow *parent, double projectRate, TrackList *list, TrackFactory *factory, SelectedRegion *selectedRegion, bool shouldPrompt=true)
Definition: Effect.cpp:1161
virtual wxString GetPreset(wxWindow *parent, const wxString &parms)
Definition: Effect.cpp:1115
Base class for many of the effects in Audacity.
Definition: Effect.h:61
int AudacityMessageBox(const wxString &message, const wxString &caption=AudacityMessageBoxCaptionStr(), long style=wxOK|wxCENTRE, wxWindow *parent=NULL, int x=wxDefaultCoord, int y=wxDefaultCoord)
Definition: ErrorDialog.h:92
virtual bool HasCurrentSettings()
Definition: Effect.cpp:1105
bool IsEffectAutomatable() const
wxString GetEffectName(const PluginID &ID)
EffectArray mRealtimeEffects
std::vector< double > mRealtimeRates
#define safenew
Definition: Audacity.h:223
wxString GetEffectIdentifier(const PluginID &ID)
Used to create a WaveTrack, or a LabelTrack.. Implementation of the functions of this class are dispe...
Definition: Track.h:850
virtual bool HasFactoryDefaults()
Definition: Effect.cpp:1110
wxCriticalSection mRealtimeLock
bool IsHidden(const PluginID &ID)
bool GetSkipStateFlag()
wxString GetEffectDescription(const PluginID &ID)
const PluginDescriptor * GetNextPlugin(PluginType type)
wxArrayString GetFactoryPresets() override
Definition: Effect.cpp:641
Defines a selected portion of a project.
const PluginDescriptor * GetFirstPlugin(PluginType type)
void RealtimeSetEffects(const EffectArray &mActive)
Effect * GetEffect(const PluginID &ID)
bool RealtimeFinalize() override
Definition: Effect.cpp:452
wxString GetName(const PluginID &ID)
void RealtimeInitialize(double rate)
bool SupportsAutomation(const PluginID &ID)
void RealtimeRemoveEffect(Effect *effect)
bool RealtimeAddProcessor(unsigned numChannels, float sampleRate) override
Definition: Effect.cpp:442
EffectManager is the class that handles effects and effect categories.
Definition: EffectManager.h:49
void UnregisterPlugin(const PluginID &ID)
static EffectManager & Get()
bool PromptUser(const PluginID &ID, wxWindow *parent)
std::vector< Effect * > EffectArray
Definition: EffectManager.h:41
wxString GetPreset(const PluginID &ID, const wxString &params, wxWindow *parent)
bool RealtimeInitialize() override
Definition: Effect.cpp:429
_("Move Track &Down")+wxT("\t")+(GetActiveProject() -> GetCommandManager() ->GetKeyFromName(wxT("TrackMoveDown"))), OnMoveTrack) POPUP_MENU_ITEM(OnMoveTopID, _("Move Track to &Top")+wxT("\t")+(GetActiveProject() ->GetCommandManager() ->GetKeyFromName(wxT("TrackMoveTop"))), OnMoveTrack) POPUP_MENU_ITEM(OnMoveBottomID, _("Move Track to &Bottom")+wxT("\t")+(GetActiveProject() ->GetCommandManager() ->GetKeyFromName(wxT("TrackMoveBottom"))), OnMoveTrack) void TrackMenuTable::OnSetName(wxCommandEvent &)
wxString GetEffectParameters(const PluginID &ID)
virtual bool IsHidden()
Definition: Effect.cpp:2475
const wxChar * name
Definition: Distortion.cpp:94
const wxString & GetID() const
const PluginID & RegisterPlugin(ModuleInterface *module) override
bool SetAutomationParameters(EffectAutomationParameters &parms) override
Definition: Effect.cpp:599
void SetSkipStateFlag(bool flag)
void UnregisterEffect(const PluginID &ID)
static const wxString kCurrentSettingsIdent
Definition: Effect.h:546
EffectOwnerMap mHostEffects
bool SupportsRealtime() override
Definition: Effect.cpp:275
bool mRealtimeSuspended
void RealtimeAddEffect(Effect *effect)
bool RealtimeIsSuspended()
const wxString & GetSymbol(const PluginID &ID)
IdentInterface * GetInstance(const PluginID &ID)
static PluginManager & Get()
int GetRealtimeLatency()
void RealtimeFinalize()
EffectDistortion::Params params
Definition: Distortion.cpp:95
virtual bool PromptUser(wxWindow *parent)
Definition: Effect.cpp:1273
const PluginDescriptor * GetPlugin(const PluginID &ID)
void RealtimeSuspend()
void RealtimeProcessEnd()
bool RealtimeIsActive()
std::vector< unsigned > mRealtimeChans
virtual wxArrayString GetUserPresets()
Definition: Effect.cpp:1094
void SetBatchProcessing(const PluginID &ID, bool start)
static const wxString kFactoryDefaultsIdent
Definition: Effect.h:547
void RealtimeProcessStart()
bool SetEffectParameters(const PluginID &ID, const wxString &params)