Audacity  2.2.2
AudioUnitEffect.cpp
Go to the documentation of this file.
1 /**********************************************************************
2 
3  Audacity: A Digital Audio Editor
4 
5  AudioUnitEffect.cpp
6 
7  Dominic Mazzoni
8  Leland Lucius
9 
10 *******************************************************************//*******************************************************************/
16 
17 #include "../../Audacity.h"
18 
19 #if USE_AUDIO_UNITS
20 
21 #include <wx/defs.h>
22 #include <wx/button.h>
23 #include <wx/control.h>
24 #include <wx/dir.h>
25 #include <wx/crt.h>
26 
27 #ifdef __WXMAC__
28 #include <wx/evtloop.h>
29 #endif
30 
31 #include <wx/filename.h>
32 #include <wx/frame.h>
33 #include <wx/listctrl.h>
34 #include <wx/sizer.h>
35 #include <wx/settings.h>
36 #include <wx/stattext.h>
37 #include <wx/textctrl.h>
38 #include <wx/tokenzr.h>
39 
40 #include "../../ShuttleGui.h"
41 #include "../../widgets/valnum.h"
42 #include "../../widgets/wxPanelWrapper.h"
43 
44 #include "AudioUnitEffect.h"
45 #include "../../Internat.h"
46 
47 struct CFReleaser
48  { void operator () (const void *p) const { if (p) CFRelease(p); } };
49 template <typename T>
50  using CFunique_ptr = std::unique_ptr<T, CFReleaser>;
51 
52 struct AudioUnitParameterInfoEx : AudioUnitParameterInfo
53 {
54  ~AudioUnitParameterInfoEx ()
55  {
56  if ((flags & kAudioUnitParameterFlag_HasCFNameString)
57  &&
58  (flags & kAudioUnitParameterFlag_CFNameRelease))
59  CFRelease( cfNameString );
60  }
61 };
62 
63 // ============================================================================
64 // Module registration entry point
65 //
66 // This is the symbol that Audacity looks for when the module is built as a
67 // dynamic library.
68 //
69 // When the module is builtin to Audacity, we use the same function, but it is
70 // declared static so as not to clash with other builtin modules.
71 // ============================================================================
72 DECLARE_MODULE_ENTRY(AudacityModule)
73 {
74  // Create and register the importer
75  // Trust the module manager not to leak this
76  return safenew AudioUnitEffectsModule(moduleManager, path);
77 }
78 
79 // ============================================================================
80 // Register this as a builtin module
81 // ============================================================================
82 DECLARE_BUILTIN_MODULE(AudioUnitEffectsBuiltin);
83 
85 //
86 // AudioUnitEffectsModule
87 //
89 
90 AudioUnitEffectsModule::AudioUnitEffectsModule(ModuleManagerInterface *moduleManager,
91  const wxString *path)
92 {
93  mModMan = moduleManager;
94  if (path)
95  {
96  mPath = *path;
97  }
98 }
99 
100 AudioUnitEffectsModule::~AudioUnitEffectsModule()
101 {
102  mPath.Clear();
103 }
104 
105 // ============================================================================
106 // IdentInterface implementation
107 // ============================================================================
108 
109 wxString AudioUnitEffectsModule::GetPath()
110 {
111  return mPath;
112 }
113 
114 wxString AudioUnitEffectsModule::GetSymbol()
115 {
116  return wxT("Audio Unit Effects");
117 }
118 
119 wxString AudioUnitEffectsModule::GetName()
120 {
121  return XO("Audio Unit Effects");
122 }
123 
124 wxString AudioUnitEffectsModule::GetVendor()
125 {
126  return XO("The Audacity Team");
127 }
128 
129 wxString AudioUnitEffectsModule::GetVersion()
130 {
131  // This "may" be different if this were to be maintained as a separate DLL
132  return AUDIOUNITEFFECTS_VERSION;
133 }
134 
135 wxString AudioUnitEffectsModule::GetDescription()
136 {
137  return _("Provides Audio Unit Effects support to Audacity");
138 }
139 
140 // ============================================================================
141 // ModuleInterface implementation
142 // ============================================================================
143 
144 wxArrayString AudioUnitEffectsModule::FileExtensions()
145 {
146  static const wxString ext[] = { _T("au") };
147  static const wxArrayString result{ sizeof(ext)/sizeof(*ext), ext };
148  return result;
149 }
150 
151 bool AudioUnitEffectsModule::Initialize()
152 {
153  // Nothing to do here
154  return true;
155 }
156 
157 void AudioUnitEffectsModule::Terminate()
158 {
159  // Nothing to do here
160  return;
161 }
162 
163 bool AudioUnitEffectsModule::AutoRegisterPlugins(PluginManagerInterface & pm)
164 {
165  // Nothing to be done here
166  return true;
167 }
168 
169 wxArrayString AudioUnitEffectsModule::FindPluginPaths(PluginManagerInterface & pm)
170 {
171  wxArrayString effects;
172 
173  LoadAudioUnitsOfType(kAudioUnitType_Effect, effects);
174  LoadAudioUnitsOfType(kAudioUnitType_Generator, effects);
175  LoadAudioUnitsOfType(kAudioUnitType_MusicEffect, effects);
176  LoadAudioUnitsOfType(kAudioUnitType_Mixer, effects);
177  LoadAudioUnitsOfType(kAudioUnitType_Panner, effects);
178 
179  return effects;
180 }
181 
182 unsigned AudioUnitEffectsModule::DiscoverPluginsAtPath(
183  const wxString & path, wxString &errMsg,
184  const RegistrationCallback &callback)
185 {
186  errMsg.clear();
187  wxString name;
188  AudioComponent component = FindAudioUnit(path, name);
189  if (component == NULL)
190  {
191  errMsg = _("Could not find component");
192  return 0;
193  }
194 
195  AudioUnitEffect effect(path, name, component);
196  if (!effect.SetHost(NULL))
197  {
198  // TODO: Is it worth it to discriminate all the ways SetHost might
199  // return false?
200  errMsg = _("Could not initialize component");
201  return 0;
202  }
203 
204  if(callback)
205  callback(this, &effect);
206 
207  return 1;
208 }
209 
210 bool AudioUnitEffectsModule::IsPluginValid(
211  const wxString & path, bool bFast)
212 {
213  if( bFast )
214  return true;
215  wxString name;
216  return FindAudioUnit(path, name) != NULL;
217 }
218 
219 IdentInterface *AudioUnitEffectsModule::CreateInstance(const wxString & path)
220 {
221  // Acquires a resource for the application.
222  wxString name;
223  AudioComponent component = FindAudioUnit(path, name);
224  if (component == NULL)
225  {
226  return NULL;
227  }
228 
229  // Safety of this depends on complementary calls to DeleteInstance on the module manager side.
230  return safenew AudioUnitEffect(path, name, component);
231 }
232 
233 void AudioUnitEffectsModule::DeleteInstance(IdentInterface *instance)
234 {
235  std::unique_ptr < AudioUnitEffect > {
236  dynamic_cast<AudioUnitEffect *>(instance)
237  };
238 }
239 
240 // ============================================================================
241 // AudioUnitEffectsModule implementation
242 // ============================================================================
243 
244 void AudioUnitEffectsModule::LoadAudioUnitsOfType(OSType inAUType,
245  wxArrayString & effects)
246 {
247  AudioComponentDescription desc;
248  AudioComponent component;
249 
250  desc.componentType = inAUType;
251  desc.componentSubType = 0;
252  desc.componentManufacturer = 0;
253  desc.componentFlags = 0;
254  desc.componentFlagsMask = 0;
255 
256  component = AudioComponentFindNext(NULL, &desc);
257  while (component != NULL)
258  {
259  OSStatus result;
260  AudioComponentDescription found;
261 
262  result = AudioComponentGetDescription(component, &found);
263  if (result == noErr)
264  {
265  CFStringRef cfName{};
266  result = AudioComponentCopyName(component, &cfName);
267  CFunique_ptr<const __CFString> uName{ cfName };
268 
269  if (result == noErr)
270  {
271  wxString name = wxCFStringRef::AsString(cfName);
272 
273  effects.Add(wxString::Format(wxT("%-4.4s/%-4.4s/%-4.4s/%s"),
274  FromOSType(found.componentManufacturer),
275  FromOSType(found.componentType),
276  FromOSType(found.componentSubType),
277  name));
278  }
279  }
280 
281  component = AudioComponentFindNext(component, &desc);
282  }
283 }
284 
285 AudioComponent AudioUnitEffectsModule::FindAudioUnit(const wxString & path,
286  wxString & name)
287 {
288  wxStringTokenizer tokens(path, wxT("/"));
289 
290  AudioComponentDescription desc;
291 
292  desc.componentManufacturer = ToOSType(tokens.GetNextToken());
293  desc.componentType = ToOSType(tokens.GetNextToken());
294  desc.componentSubType = ToOSType(tokens.GetNextToken());
295  desc.componentFlags = 0;
296  desc.componentFlagsMask = 0;
297 
298  name = tokens.GetNextToken();
299 
300  return AudioComponentFindNext(NULL, &desc);
301 }
302 
303 wxString AudioUnitEffectsModule::FromOSType(OSType type)
304 {
305  OSType rev = (type & 0xff000000) >> 24 |
306  (type & 0x00ff0000) >> 8 |
307  (type & 0x0000ff00) << 8 |
308  (type & 0x000000ff) << 24;
309 
310  return wxString::FromUTF8((char *)&rev, 4);
311 }
312 
313 OSType AudioUnitEffectsModule::ToOSType(const wxString & type)
314 {
315  wxCharBuffer buf = type.ToUTF8();
316 
317  OSType rev = ((unsigned char)buf.data()[0]) << 24 |
318  ((unsigned char)buf.data()[1]) << 16 |
319  ((unsigned char)buf.data()[2]) << 8 |
320  ((unsigned char)buf.data()[3]);
321 
322  return rev;
323 }
324 
326 //
327 // AudioUnitEffectOptionsDialog
328 //
330 
331 class AudioUnitEffectOptionsDialog final : public wxDialogWrapper
332 {
333 public:
334  AudioUnitEffectOptionsDialog(wxWindow * parent, EffectHostInterface *host);
335  virtual ~AudioUnitEffectOptionsDialog();
336 
337  void PopulateOrExchange(ShuttleGui & S);
338 
339  void OnOk(wxCommandEvent & evt);
340 
341 private:
342  EffectHostInterface *mHost;
343 
344  bool mUseLatency;
345  wxString mUIType;
346 
347  wxArrayString mUITypes;
348 
349  DECLARE_EVENT_TABLE()
350 };
351 
352 BEGIN_EVENT_TABLE(AudioUnitEffectOptionsDialog, wxDialogWrapper)
353  EVT_BUTTON(wxID_OK, AudioUnitEffectOptionsDialog::OnOk)
355 
356 AudioUnitEffectOptionsDialog::AudioUnitEffectOptionsDialog(wxWindow * parent, EffectHostInterface *host)
357 : wxDialogWrapper(parent, wxID_ANY, wxString(_("Audio Unit Effect Options")))
358 {
359  mHost = host;
360 
361  mUITypes.Add(_("Full"));
362  mUITypes.Add(_("Generic"));
363  mUITypes.Add(_("Basic"));
364 
365  mHost->GetSharedConfig(wxT("Options"), wxT("UseLatency"), mUseLatency, true);
366  mHost->GetSharedConfig(wxT("Options"), wxT("UIType"), mUIType, wxT("Full"));
367 
368  mUIType = wxGetTranslation(mUIType);
369 
370  ShuttleGui S(this, eIsCreating);
371  PopulateOrExchange(S);
372 }
373 
374 AudioUnitEffectOptionsDialog::~AudioUnitEffectOptionsDialog()
375 {
376 }
377 
378 void AudioUnitEffectOptionsDialog::PopulateOrExchange(ShuttleGui & S)
379 {
380 
381  S.SetBorder(5);
382  S.StartHorizontalLay(wxEXPAND, 1);
383  {
384  S.StartVerticalLay(false);
385  {
386  S.StartStatic(_("Latency Compensation"));
387  {
388  S.AddVariableText(wxString() +
389  _("As part of their processing, some Audio Unit effects must delay returning ") +
390  _("audio to Audacity. When not compensating for this delay, you will ") +
391  _("notice that small silences have been inserted into the audio. ") +
392  _("Enabling this option will provide that compensation, but it may ") +
393  _("not work for all Audio Unit effects."))->Wrap(650);
394 
395  S.StartHorizontalLay(wxALIGN_LEFT);
396  {
397  S.TieCheckBox(_("Enable &compensation"),
398  mUseLatency);
399  }
400  S.EndHorizontalLay();
401  }
402  S.EndStatic();
403 
404  S.StartStatic(_("User Interface"));
405  {
406  S.AddVariableText(wxString() +
407  _("Select \"Full\" to use the graphical interface if supplied by the Audio Unit.") +
408  _(" Select \"Generic\" to use the system supplied generic interface.") +
409  _(" Select \"Basic\" for a basic text-only interface. ") +
410  _(" Reopen the effect for this to take effect."))->Wrap(650);
411 
412  S.StartHorizontalLay(wxALIGN_LEFT);
413  {
414  S.TieChoice(_("Select &interface"),
415  mUIType,
416  &mUITypes);
417  }
418  S.EndHorizontalLay();
419  }
420  S.EndStatic();
421  }
422  S.EndVerticalLay();
423  }
424  S.EndHorizontalLay();
425 
426  S.AddStandardButtons();
427 
428  Layout();
429  Fit();
430  Center();
431 }
432 
433 void AudioUnitEffectOptionsDialog::OnOk(wxCommandEvent & WXUNUSED(evt))
434 {
435  if (!Validate())
436  {
437  return;
438  }
439 
441  PopulateOrExchange(S);
442 
443  if (mUIType == _("Full"))
444  {
445  mUIType = wxT("Full");
446  }
447  else if (mUIType == _("Generic"))
448  {
449  mUIType = wxT("Generic");
450  }
451  else if (mUIType == _("Basic"))
452  {
453  mUIType = wxT("Basic");
454  }
455 
456  mHost->SetSharedConfig(wxT("Options"), wxT("UseLatency"), mUseLatency);
457  mHost->SetSharedConfig(wxT("Options"), wxT("UIType"), mUIType);
458 
459  EndModal(wxID_OK);
460 }
461 
463 //
464 // AudioUnitEffectExportDialog
465 //
467 
468 #define PRESET_LOCAL_PATH wxT("/Library/Audio/Presets")
469 #define PRESET_USER_PATH wxT("~/Library/Audio/Presets")
470 
471 class AudioUnitEffectExportDialog final : public wxDialogWrapper
472 {
473 public:
474  AudioUnitEffectExportDialog(wxWindow * parent, AudioUnitEffect *effect);
475  virtual ~AudioUnitEffectExportDialog();
476 
477  void PopulateOrExchange(ShuttleGui & S);
478 
479  void OnOk(wxCommandEvent & evt);
480 
481 private:
482  wxWindow *mParent;
483  AudioUnitEffect *mEffect;
484 
485  wxListCtrl *mList;
486 
487  DECLARE_EVENT_TABLE()
488 };
489 
490 BEGIN_EVENT_TABLE(AudioUnitEffectExportDialog, wxDialogWrapper)
491  EVT_BUTTON(wxID_OK, AudioUnitEffectExportDialog::OnOk)
493 
494 AudioUnitEffectExportDialog::AudioUnitEffectExportDialog(wxWindow * parent, AudioUnitEffect *effect)
495 : wxDialogWrapper(parent, wxID_ANY, wxString(_("Export Audio Unit Presets")))
496 {
497  mEffect = effect;
498 
499  ShuttleGui S(this, eIsCreating);
500  PopulateOrExchange(S);
501 }
502 
503 AudioUnitEffectExportDialog::~AudioUnitEffectExportDialog()
504 {
505 }
506 
507 void AudioUnitEffectExportDialog::PopulateOrExchange(ShuttleGui & S)
508 {
509  S.SetBorder(5);
510  S.StartHorizontalLay(wxEXPAND, 1);
511  {
512  S.StartVerticalLay(true);
513  {
514  S.StartStatic(_("Presets (may select multiple)"));
515  {
516  S.SetStyle(wxLC_REPORT | wxLC_HRULES | wxLC_VRULES |
517  wxLC_NO_SORT_HEADER);
518  mList = S.AddListControlReportMode();
519  mList->InsertColumn(0, _("Preset"), wxLIST_FORMAT_LEFT);
520  }
521  S.EndStatic();
522  }
523  S.EndVerticalLay();
524  }
525  S.EndHorizontalLay();
526 
527  S.AddStandardButtons();
528 
529  wxArrayString presets;
530 
531  mEffect->mHost->GetPrivateConfigSubgroups(mEffect->mHost->GetUserPresetsGroup(wxEmptyString), presets);
532 
533  presets.Sort();
534 
535  for (size_t i = 0, cnt = presets.GetCount(); i < cnt; i++)
536  {
537  mList->InsertItem(i, presets[i]);
538  }
539 
540  mList->SetColumnWidth(0, wxLIST_AUTOSIZE);
541 
542  // Set the list size...with a little extra for good measure
543  wxSize sz = mList->GetBestSize();
544  sz.x += 5;
545  sz.y += 5;
546  mList->SetMinSize(sz);
547 
548  Layout();
549  Fit();
550  Center();
551 }
552 
553 void AudioUnitEffectExportDialog::OnOk(wxCommandEvent & WXUNUSED(evt))
554 {
555  // Save active settings
556  wxString settingsName(wxT("Export Save"));
557  mEffect->SaveParameters(settingsName);
558 
559  // Look for selected presets
560  long sel = mList->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
561  while (sel >= 0)
562  {
563  wxString name = mList->GetItemText(sel);
564 
565  // Make the preset current
566  mEffect->LoadParameters(mEffect->mHost->GetUserPresetsGroup(name));
567 
568  // Make sure the user preset directory exists
569  wxString path;
570  path.Printf(wxT("%s/%s/%s/%s.aupreset"),
571  PRESET_USER_PATH,
572  mEffect->mVendor,
573  mEffect->mName,
574  name);
575  wxFileName fn(path);
576  fn.Normalize();
577  fn.Mkdir(0755, wxPATH_MKDIR_FULL);
578  path = fn.GetFullPath();
579 
580  // First set the name of the preset
581  wxCFStringRef cfname(name);
582 
583  AUPreset preset;
584  preset.presetNumber = -1; // indicates user preset
585  preset.presetName = cfname;
586 
587  AudioUnitSetProperty(mEffect->mUnit,
588  kAudioUnitProperty_PresentPreset,
589  kAudioUnitScope_Global,
590  0,
591  &preset,
592  sizeof(preset));
593 
594  // Now retrieve the preset content
595  CFPropertyListRef content;
596  UInt32 size = sizeof(content);
597  AudioUnitGetProperty(mEffect->mUnit,
598  kAudioUnitProperty_ClassInfo,
599  kAudioUnitScope_Global,
600  0,
601  &content,
602  &size);
603 
604  // And convert it to XML
605  CFunique_ptr<const __CFData> xml {
606  CFPropertyListCreateXMLData(kCFAllocatorDefault, content)
607  };
608  if (xml)
609  {
610  // Create the CFURL for the path
611  CFunique_ptr<const __CFURL> url {
612  CFURLCreateWithFileSystemPath(kCFAllocatorDefault,
613  wxCFStringRef(path),
614  kCFURLPOSIXPathStyle,
615  false)
616  };
617  if (url)
618  {
619  SInt32 error;
620  Boolean res = CFURLWriteDataAndPropertiesToResource(url.get(),
621  xml.get(),
622  NULL,
623  &error);
624  }
625  }
626 
627  // And continue to the next selected preset
628  sel = mList->GetNextItem(sel, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
629  }
630 
631  // Restore active settings
632  mEffect->LoadParameters(settingsName);
633  mEffect->mHost->RemovePrivateConfigSubgroup(settingsName);
634 
635  EndModal(wxID_OK);
636 }
637 
639 //
640 // AudioUnitEffectImportDialog
641 //
643 
644 class AudioUnitEffectImportDialog final : public wxDialogWrapper
645 {
646 public:
647  AudioUnitEffectImportDialog(wxWindow * parent, AudioUnitEffect *effect);
648  virtual ~AudioUnitEffectImportDialog();
649 
650  void PopulateOrExchange(ShuttleGui & S);
651 
652  void OnOk(wxCommandEvent & evt);
653 
654 private:
655  wxWindow *mParent;
656  AudioUnitEffect *mEffect;
657 
658  wxListCtrl *mList;
659 
660  DECLARE_EVENT_TABLE()
661 };
662 
663 BEGIN_EVENT_TABLE(AudioUnitEffectImportDialog, wxDialogWrapper)
664  EVT_BUTTON(wxID_OK, AudioUnitEffectImportDialog::OnOk)
666 
667 AudioUnitEffectImportDialog::AudioUnitEffectImportDialog(wxWindow * parent, AudioUnitEffect *effect)
668 : wxDialogWrapper(parent, wxID_ANY, wxString(_("Import Audio Unit Presets")))
669 {
670  mEffect = effect;
671 
672  ShuttleGui S(this, eIsCreating);
673  PopulateOrExchange(S);
674 }
675 
676 AudioUnitEffectImportDialog::~AudioUnitEffectImportDialog()
677 {
678 }
679 
680 void AudioUnitEffectImportDialog::PopulateOrExchange(ShuttleGui & S)
681 {
682  S.SetBorder(5);
683  S.StartHorizontalLay(wxEXPAND, 1);
684  {
685  S.StartVerticalLay(true);
686  {
687  S.StartStatic(_("Presets (may select multiple)"));
688  {
689  S.SetStyle(wxLC_REPORT | wxLC_HRULES | wxLC_VRULES |
690  wxLC_NO_SORT_HEADER);
691  mList = S.AddListControlReportMode();
692  mList->InsertColumn(0, _("Preset"), wxLIST_FORMAT_LEFT);
693  mList->InsertColumn(1, _("Location"), wxLIST_FORMAT_LEFT);
694  }
695  S.EndStatic();
696  }
697  S.EndVerticalLay();
698  }
699  S.EndHorizontalLay();
700 
701  S.AddStandardButtons();
702 
703  wxArrayString presets;
704 
705  // Make sure the user preset directory exists
706  wxString path;
707  path.Printf(wxT("%s/%s/%s"),
708  PRESET_LOCAL_PATH,
709  mEffect->mVendor,
710  mEffect->mName);
711  wxFileName fn(path);
712  fn.Normalize();
713 
714  // Get all presets in the local domain for this effect
715  wxDir::GetAllFiles(fn.GetFullPath(), &presets, wxT("*.aupreset"));
716 
717  path.Printf(wxT("%s/%s/%s"),
718  PRESET_USER_PATH,
719  mEffect->mVendor,
720  mEffect->mName);
721  fn = path;
722  fn.Normalize();
723 
724  // Get all presets in the user domain for this effect
725  wxDir::GetAllFiles(fn.GetFullPath(), &presets, wxT("*.aupreset"));
726 
727  presets.Sort();
728 
729  for (size_t i = 0, cnt = presets.GetCount(); i < cnt; i++)
730  {
731  fn = presets[i];
732  mList->InsertItem(i, fn.GetName());
733  mList->SetItem(i, 1, fn.GetPath());
734  }
735 
736  mList->SetColumnWidth(0, wxLIST_AUTOSIZE);
737  mList->SetColumnWidth(1, wxLIST_AUTOSIZE);
738 
739  // Set the list size...with a little extra for good measure
740  wxSize sz = mList->GetBestSize();
741  sz.x += 5;
742  sz.y += 5;
743  mList->SetMinSize(sz);
744 
745  Layout();
746  Fit();
747  Center();
748 }
749 
750 void AudioUnitEffectImportDialog::OnOk(wxCommandEvent & WXUNUSED(evt))
751 {
752  // Save active settings
753  wxString settingsName(wxT("Import Save"));
754  mEffect->SaveParameters(settingsName);
755 
756  // Look for selected presets
757  long sel = -1;
758  while ((sel = mList->GetNextItem(sel, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED)) >= 0)
759  {
760  wxListItem item;
761  item.SetId(sel);
762  item.SetColumn(1);
763  item.SetMask(wxLIST_MASK_TEXT);
764  mList->GetItem(item);
765 
766  wxString path;
767  path.Printf(wxT("%s/%s.aupreset"),
768  item.GetText(),
769  mList->GetItemText(sel));
770 
771  // Create the CFURL for the path
772  CFunique_ptr<const __CFURL> url {
773  CFURLCreateWithFileSystemPath(kCFAllocatorDefault,
774  wxCFStringRef(path),
775  kCFURLPOSIXPathStyle,
776  false)
777  };
778 
779  if (!url)
780  {
781  continue;
782  }
783 
784  CFDataRef xml{};
785  SInt32 error;
786  Boolean res = CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault,
787  url.get(),
788  &xml,
789  NULL,
790  NULL,
791  &error);
792  CFunique_ptr<const __CFData> uxml { xml };
793 
794  if (!res)
795  {
796  continue;
797  }
798 
799  CFunique_ptr<char> content {
800  (char*)CFPropertyListCreateFromXMLData(kCFAllocatorDefault,
801  xml,
802  kCFPropertyListImmutable,
803  NULL)
804  };
805 
806  if (!content)
807  {
808  continue;
809  }
810 
811  OSStatus result = AudioUnitSetProperty(mEffect->mUnit,
812  kAudioUnitProperty_ClassInfo,
813  kAudioUnitScope_Global,
814  0,
815  &content,
816  sizeof(content));
817 
818  mEffect->SaveUserPreset(mEffect->mHost->GetUserPresetsGroup(mList->GetItemText(sel)));
819  }
820 
821  // Restore active settings
822  mEffect->LoadParameters(settingsName);
823  mEffect->mHost->RemovePrivateConfigSubgroup(settingsName);
824 
825  EndModal(wxID_OK);
826 }
827 
829 //
830 // AudioUnitEffect
831 //
833 
834 AudioUnitEffect::AudioUnitEffect(const wxString & path,
835  const wxString & name,
836  AudioComponent component,
837  AudioUnitEffect *master)
838 {
839  mPath = path;
840  mName = name.AfterFirst(wxT(':')).Trim(true).Trim(false);
841  mVendor = name.BeforeFirst(wxT(':')).Trim(true).Trim(false);
842  mComponent = component;
843  mMaster = master;
844 
845  mUnit = NULL;
846 
847  mBlockSize = 0.0;
848  mInteractive = false;
849  mIsGraphical = false;
850 
851  mUIHost = NULL;
852  mDialog = NULL;
853  mParent = NULL;
854 
855  mUnitInitialized = false;
856 
857  mEventListenerRef = NULL;
858 }
859 
860 AudioUnitEffect::~AudioUnitEffect()
861 {
862  if (mUnitInitialized)
863  {
864  AudioUnitUninitialize(mUnit);
865  }
866 
867  if (mEventListenerRef)
868  {
869  AUListenerDispose(mEventListenerRef);
870  }
871 
872  if (mUnit)
873  {
874  AudioComponentInstanceDispose(mUnit);
875  }
876 }
877 
878 // ============================================================================
879 // IdentInterface implementation
880 // ============================================================================
881 
882 wxString AudioUnitEffect::GetPath()
883 {
884  return mPath;
885 }
886 
887 wxString AudioUnitEffect::GetSymbol()
888 {
889  return mName;
890 }
891 
892 wxString AudioUnitEffect::GetName()
893 {
894  return GetSymbol();
895 }
896 
897 wxString AudioUnitEffect::GetVendor()
898 {
899  return mVendor;
900 }
901 
902 wxString AudioUnitEffect::GetVersion()
903 {
904  UInt32 version;
905 
906  OSStatus result = AudioComponentGetVersion(mComponent, &version);
907 
908  return wxString::Format(wxT("%d.%d.%d"),
909  (version >> 16) & 0xffff,
910  (version >> 8) & 0xff,
911  version & 0xff);
912 }
913 
914 wxString AudioUnitEffect::GetDescription()
915 {
916  /* i18n-hint: Can mean "not available," "not applicable," "no answer" */
917  return _("n/a");
918 }
919 
920 // ============================================================================
921 // EffectIdentInterface implementation
922 // ============================================================================
923 
924 EffectType AudioUnitEffect::GetType()
925 {
926  if (mAudioIns == 0 && mAudioOuts == 0)
927  {
928  return EffectTypeNone;
929  }
930 
931  if (mAudioIns == 0)
932  {
933  return EffectTypeGenerate;
934  }
935 
936  if (mAudioOuts == 0)
937  {
938  return EffectTypeAnalyze;
939  }
940 
941  return EffectTypeProcess;
942 }
943 
944 wxString AudioUnitEffect::GetFamilyId()
945 {
946  return AUDIOUNITEFFECTS_FAMILY;
947 }
948 
949 wxString AudioUnitEffect::GetFamilyName()
950 {
951  return AUDIOUNITEFFECTS_FAMILY;
952 }
953 
954 bool AudioUnitEffect::IsInteractive()
955 {
956  return mInteractive;
957 }
958 
959 bool AudioUnitEffect::IsDefault()
960 {
961  return false;
962 }
963 
964 bool AudioUnitEffect::IsLegacy()
965 {
966  return false;
967 }
968 
969 bool AudioUnitEffect::SupportsRealtime()
970 {
971  return GetType() == EffectTypeProcess;
972 }
973 
974 bool AudioUnitEffect::SupportsAutomation()
975 {
976  OSStatus result;
977  UInt32 dataSize;
978  Boolean isWritable;
979 
980  result = AudioUnitGetPropertyInfo(mUnit,
981  kAudioUnitProperty_ParameterList,
982  kAudioUnitScope_Global,
983  0,
984  &dataSize,
985  &isWritable);
986  if (result != noErr)
987  {
988  return false;
989  }
990 
991  UInt32 cnt = dataSize / sizeof(AudioUnitParameterID);
993 
994  result = AudioUnitGetProperty(mUnit,
995  kAudioUnitProperty_ParameterList,
996  kAudioUnitScope_Global,
997  0,
998  array.get(),
999  &dataSize);
1000  if (result != noErr)
1001  {
1002  return false;
1003  }
1004 
1005  for (int i = 0; i < cnt; i++)
1006  {
1007  AudioUnitParameterInfoEx info;
1008  dataSize = sizeof(info);
1009  result = AudioUnitGetProperty(mUnit,
1010  kAudioUnitProperty_ParameterInfo,
1011  kAudioUnitScope_Global,
1012  array[i],
1013  &info,
1014  &dataSize);
1015  if (result != noErr)
1016  {
1017  return false;
1018  }
1019 
1020  if (info.flags & kAudioUnitParameterFlag_IsWritable)
1021  {
1022  // All we need is one
1023  return true;
1024  }
1025  }
1026 
1027  return false;
1028 }
1029 
1030 // ============================================================================
1031 // EffectClientInterface Implementation
1032 // ============================================================================
1033 
1034 bool AudioUnitEffect::SetHost(EffectHostInterface *host)
1035 {
1036  OSStatus result;
1037 
1038  mHost = host;
1039 
1040  mSampleRate = 44100;
1041  result = AudioComponentInstanceNew(mComponent, &mUnit);
1042  if (!mUnit)
1043  {
1044  return false;
1045  }
1046 
1047  GetChannelCounts();
1048 
1049  SetRateAndChannels();
1050 
1051  // Retrieve the desired number of frames per slice
1052  UInt32 dataSize = sizeof(mBlockSize);
1053  mBlockSize = 512;
1054  AudioUnitGetProperty(mUnit,
1055  kAudioUnitProperty_MaximumFramesPerSlice,
1056  kAudioUnitScope_Global,
1057  0,
1058  &mBlockSize,
1059  &dataSize);
1060 
1061  // mHost will be null during registration
1062  if (mHost)
1063  {
1064  mHost->GetSharedConfig(wxT("Options"), wxT("UseLatency"), mUseLatency, true);
1065  mHost->GetSharedConfig(wxT("Options"), wxT("UIType"), mUIType, wxT("Full"));
1066 
1067  mUIType = wxGetTranslation(mUIType);
1068 
1069  bool haveDefaults;
1070  mHost->GetPrivateConfig(mHost->GetFactoryDefaultsGroup(), wxT("Initialized"), haveDefaults, false);
1071  if (!haveDefaults)
1072  {
1073  SaveParameters(mHost->GetFactoryDefaultsGroup());
1074  mHost->SetPrivateConfig(mHost->GetFactoryDefaultsGroup(), wxT("Initialized"), true);
1075  }
1076 
1077  LoadParameters(mHost->GetCurrentSettingsGroup());
1078  }
1079 
1080  if (!mMaster)
1081  {
1082  result = AUEventListenerCreate(AudioUnitEffect::EventListenerCallback,
1083  this,
1084  (CFRunLoopRef)GetCFRunLoopFromEventLoop(GetCurrentEventLoop()),
1085  kCFRunLoopDefaultMode,
1086  0.0,
1087  0.0,
1088  &mEventListenerRef);
1089  if (result != noErr)
1090  {
1091  return false;
1092  }
1093 
1094  AudioUnitEvent event;
1095 
1096  event.mEventType = kAudioUnitEvent_ParameterValueChange;
1097  event.mArgument.mParameter.mAudioUnit = mUnit;
1098  event.mArgument.mParameter.mScope = kAudioUnitScope_Global;
1099  event.mArgument.mParameter.mElement = 0;
1100 
1101  UInt32 dataSize;
1102  Boolean isWritable;
1103 
1104  // Retrieve the list of properties
1105  result = AudioUnitGetPropertyInfo(mUnit,
1106  kAudioUnitProperty_ParameterList,
1107  kAudioUnitScope_Global,
1108  0,
1109  &dataSize,
1110  &isWritable);
1111  if (result != noErr)
1112  {
1113  return false;
1114  }
1115 
1116  // And get them
1117  UInt32 cnt = dataSize / sizeof(AudioUnitParameterID);
1118  ArrayOf<AudioUnitParameterID> array {cnt};
1119 
1120  result = AudioUnitGetProperty(mUnit,
1121  kAudioUnitProperty_ParameterList,
1122  kAudioUnitScope_Global,
1123  0,
1124  array.get(),
1125  &dataSize);
1126  if (result != noErr)
1127  {
1128  return false;
1129  }
1130 
1131  // Register them as something we're interested in
1132  for (int i = 0; i < cnt; i++)
1133  {
1134  event.mArgument.mParameter.mParameterID = array[i];
1135  result = AUEventListenerAddEventType(mEventListenerRef,
1136  this,
1137  &event);
1138  if (result != noErr)
1139  {
1140  return false;
1141  }
1142  }
1143 
1144  event.mEventType = kAudioUnitEvent_PropertyChange;
1145  event.mArgument.mProperty.mAudioUnit = mUnit;
1146  event.mArgument.mProperty.mPropertyID = kAudioUnitProperty_Latency;
1147  event.mArgument.mProperty.mScope = kAudioUnitScope_Global;
1148  event.mArgument.mProperty.mElement = 0;
1149 
1150  result = AUEventListenerAddEventType(mEventListenerRef,
1151  this,
1152  &event);
1153  if (result != noErr)
1154  {
1155  return false;
1156  }
1157 
1158  AudioUnitCocoaViewInfo cocoaViewInfo;
1159  dataSize = sizeof(AudioUnitCocoaViewInfo);
1160 
1161  // Check for a Cocoa UI
1162  result = AudioUnitGetProperty(mUnit,
1163  kAudioUnitProperty_CocoaUI,
1164  kAudioUnitScope_Global,
1165  0,
1166  &cocoaViewInfo,
1167  &dataSize);
1168 
1169  bool hasCocoa = result == noErr;
1170 
1171  // Check for a Carbon UI
1172  AudioComponentDescription compDesc;
1173  dataSize = sizeof(compDesc);
1174  result = AudioUnitGetProperty(mUnit,
1175  kAudioUnitProperty_GetUIComponentList,
1176  kAudioUnitScope_Global,
1177  0,
1178  &compDesc,
1179  &dataSize);
1180  bool hasCarbon = result == noErr;
1181 
1182  mInteractive = (cnt > 0) || hasCocoa || hasCarbon;
1183  }
1184 
1185  return true;
1186 }
1187 
1188 unsigned AudioUnitEffect::GetAudioInCount()
1189 {
1190  return mAudioIns;
1191 }
1192 
1193 unsigned AudioUnitEffect::GetAudioOutCount()
1194 {
1195  return mAudioOuts;
1196 }
1197 
1198 int AudioUnitEffect::GetMidiInCount()
1199 {
1200  return 0;
1201 }
1202 
1203 int AudioUnitEffect::GetMidiOutCount()
1204 {
1205  return 0;
1206 }
1207 
1208 void AudioUnitEffect::SetSampleRate(double rate)
1209 {
1210  mSampleRate = rate;
1211 }
1212 
1213 size_t AudioUnitEffect::SetBlockSize(size_t maxBlockSize)
1214 {
1215  return mBlockSize;
1216 }
1217 
1218 sampleCount AudioUnitEffect::GetLatency()
1219 {
1220  // Retrieve the latency (can be updated via an event)
1221  if (mUseLatency && !mLatencyDone)
1222  {
1223  mLatencyDone = true;
1224 
1225  Float64 latency = 0.0;
1226  UInt32 dataSize = sizeof(latency);
1227  AudioUnitGetProperty(mUnit,
1228  kAudioUnitProperty_Latency,
1229  kAudioUnitScope_Global,
1230  0,
1231  &latency,
1232  &dataSize);
1233 
1234  return sampleCount( latency * mSampleRate );
1235  }
1236 
1237  return 0;
1238 }
1239 
1240 size_t AudioUnitEffect::GetTailSize()
1241 {
1242  // Retrieve the tail time
1243  Float64 tailTime = 0.0;
1244  UInt32 dataSize = sizeof(tailTime);
1245  AudioUnitGetProperty(mUnit,
1246  kAudioUnitProperty_TailTime,
1247  kAudioUnitScope_Global,
1248  0,
1249  &tailTime,
1250  &dataSize);
1251 
1252  return tailTime * mSampleRate;
1253 }
1254 
1255 bool AudioUnitEffect::IsReady()
1256 {
1257  return mReady;
1258 }
1259 
1260 bool AudioUnitEffect::ProcessInitialize(sampleCount WXUNUSED(totalLen), ChannelNames WXUNUSED(chanMap))
1261 {
1262  OSStatus result;
1263 
1264  mInputList.reinit( mAudioIns );
1265  mInputList[0].mNumberBuffers = mAudioIns;
1266 
1267  mOutputList.reinit( mAudioOuts );
1268  mOutputList[0].mNumberBuffers = mAudioOuts;
1269 
1270  memset(&mTimeStamp, 0, sizeof(AudioTimeStamp));
1271  mTimeStamp.mSampleTime = 0; // This is a double-precision number that should
1272  // accumulate the number of frames processed so far
1273  mTimeStamp.mFlags = kAudioTimeStampSampleTimeValid;
1274 
1275  if (!SetRateAndChannels())
1276  {
1277  return false;
1278  }
1279 
1280  AURenderCallbackStruct callbackStruct;
1281  callbackStruct.inputProc = RenderCallback;
1282  callbackStruct.inputProcRefCon = this;
1283  result = AudioUnitSetProperty(mUnit,
1284  kAudioUnitProperty_SetRenderCallback,
1285  kAudioUnitScope_Input,
1286  0,
1287  &callbackStruct,
1288  sizeof(AURenderCallbackStruct));
1289  if (result != noErr)
1290  {
1291  wxPrintf("Setting input render callback failed.\n");
1292  return false;
1293  }
1294 
1295  result = AudioUnitReset(mUnit, kAudioUnitScope_Global, 0);
1296  if (result != noErr)
1297  {
1298  return false;
1299  }
1300 
1301  mLatencyDone = false;
1302 
1303  mReady = true;
1304 
1305  return true;
1306 }
1307 
1308 bool AudioUnitEffect::ProcessFinalize()
1309 {
1310  mReady = false;
1311 
1312  mOutputList.reset();
1313  mInputList.reset();
1314 
1315  return true;
1316 }
1317 
1318 size_t AudioUnitEffect::ProcessBlock(float **inBlock, float **outBlock, size_t blockLen)
1319 {
1320  for (size_t i = 0; i < mAudioIns; i++)
1321  {
1322  mInputList[0].mBuffers[i].mNumberChannels = 1;
1323  mInputList[0].mBuffers[i].mData = inBlock[i];
1324  mInputList[0].mBuffers[i].mDataByteSize = sizeof(float) * blockLen;
1325  }
1326 
1327  for (size_t i = 0; i < mAudioOuts; i++)
1328  {
1329  mOutputList[0].mBuffers[i].mNumberChannels = 1;
1330  mOutputList[0].mBuffers[i].mData = outBlock[i];
1331  mOutputList[0].mBuffers[i].mDataByteSize = sizeof(float) * blockLen;
1332  }
1333 
1334  AudioUnitRenderActionFlags flags = 0;
1335  OSStatus result;
1336 
1337  result = AudioUnitRender(mUnit,
1338  &flags,
1339  &mTimeStamp,
1340  0,
1341  blockLen,
1342  mOutputList.get());
1343  if (result != noErr)
1344  {
1345  wxPrintf("Render failed: %d %4.4s\n", (int)result, (char *)&result);
1346  return 0;
1347  }
1348 
1349  mTimeStamp.mSampleTime += blockLen;
1350 
1351  return blockLen;
1352 }
1353 
1354 bool AudioUnitEffect::RealtimeInitialize()
1355 {
1356  mMasterIn.reinit(mAudioIns, mBlockSize, true);
1357  mMasterOut.reinit( mAudioOuts, mBlockSize );
1358  return ProcessInitialize(0);
1359 }
1360 
1361 bool AudioUnitEffect::RealtimeAddProcessor(unsigned numChannels, float sampleRate)
1362 {
1363  auto slave = make_movable<AudioUnitEffect>(mPath, mName, mComponent, this);
1364  if (!slave->SetHost(NULL))
1365  return false;
1366 
1367  slave->SetBlockSize(mBlockSize);
1368  slave->SetChannelCount(numChannels);
1369  slave->SetSampleRate(sampleRate);
1370 
1371  if (!CopyParameters(mUnit, slave->mUnit))
1372  return false;
1373 
1374  auto pSlave = slave.get();
1375  mSlaves.push_back(std::move(slave));
1376 
1377  return pSlave->ProcessInitialize(0);
1378 }
1379 
1380 bool AudioUnitEffect::RealtimeFinalize()
1381 {
1382  for (size_t i = 0, cnt = mSlaves.size(); i < cnt; i++)
1383  mSlaves[i]->ProcessFinalize();
1384  mSlaves.clear();
1385 
1386  mMasterIn.reset();
1387  mMasterOut.reset();
1388 
1389  return ProcessFinalize();
1390 }
1391 
1392 bool AudioUnitEffect::RealtimeSuspend()
1393 {
1394  return true;
1395 }
1396 
1397 bool AudioUnitEffect::RealtimeResume()
1398 {
1399  OSStatus result;
1400 
1401  result = AudioUnitReset(mUnit, kAudioUnitScope_Global, 0);
1402  if (result != noErr)
1403  {
1404  return false;
1405  }
1406 
1407  return true;
1408 }
1409 
1410 bool AudioUnitEffect::RealtimeProcessStart()
1411 {
1412  for (size_t i = 0; i < mAudioIns; i++)
1413  memset(mMasterIn[i].get(), 0, mBlockSize * sizeof(float));
1414 
1415  mNumSamples = 0;
1416 
1417  return true;
1418 }
1419 
1420 size_t AudioUnitEffect::RealtimeProcess(int group,
1421  float **inbuf,
1422  float **outbuf,
1423  size_t numSamples)
1424 {
1425  wxASSERT(numSamples <= mBlockSize);
1426 
1427  for (size_t c = 0; c < mAudioIns; c++)
1428  {
1429  for (decltype(numSamples) s = 0; s < numSamples; s++)
1430  {
1431  mMasterIn[c][s] += inbuf[c][s];
1432  }
1433  }
1434  mNumSamples = wxMax(numSamples, mNumSamples);
1435 
1436  return mSlaves[group]->ProcessBlock(inbuf, outbuf, numSamples);
1437 }
1438 
1439 bool AudioUnitEffect::RealtimeProcessEnd()
1440 {
1441  ProcessBlock(
1442  reinterpret_cast<float**>(mMasterIn.get()),
1443  reinterpret_cast<float**>(mMasterOut.get()),
1444  mNumSamples);
1445 
1446  return true;
1447 }
1448 
1449 bool AudioUnitEffect::ShowInterface(wxWindow *parent, bool forceModal)
1450 {
1451  if (mDialog)
1452  {
1453  if( mDialog->Close(true) )
1454  mDialog = nullptr;
1455  return false;
1456  }
1457 
1458  // mDialog is null
1459  auto cleanup = valueRestorer( mDialog );
1460 
1461  mDialog = mHost->CreateUI(parent, this);
1462  if (!mDialog)
1463  {
1464  return false;
1465  }
1466 
1467  if ((SupportsRealtime() || GetType() == EffectTypeAnalyze) && !forceModal)
1468  {
1469  mDialog->Show();
1470  cleanup.release();
1471 
1472  return false;
1473  }
1474 
1475  bool res = mDialog->ShowModal() != 0;
1476 
1477  return res;
1478 }
1479 
1480 bool AudioUnitEffect::GetAutomationParameters(EffectAutomationParameters & parms)
1481 {
1482  OSStatus result;
1483  UInt32 dataSize;
1484  Boolean isWritable;
1485 
1486  result = AudioUnitGetPropertyInfo(mUnit,
1487  kAudioUnitProperty_ParameterList,
1488  kAudioUnitScope_Global,
1489  0,
1490  &dataSize,
1491  &isWritable);
1492  if (result != noErr)
1493  {
1494  return false;
1495  }
1496 
1497  UInt32 cnt = dataSize / sizeof(AudioUnitParameterID);
1498  ArrayOf<AudioUnitParameterID> array {cnt};
1499 
1500  result = AudioUnitGetProperty(mUnit,
1501  kAudioUnitProperty_ParameterList,
1502  kAudioUnitScope_Global,
1503  0,
1504  array.get(),
1505  &dataSize);
1506  if (result != noErr)
1507  {
1508  return false;
1509  }
1510 
1511  for (int i = 0; i < cnt; i++)
1512  {
1513  AudioUnitParameterInfoEx info;
1514  dataSize = sizeof(info);
1515  result = AudioUnitGetProperty(mUnit,
1516  kAudioUnitProperty_ParameterInfo,
1517  kAudioUnitScope_Global,
1518  array[i],
1519  &info,
1520  &dataSize);
1521  if (result != noErr)
1522  {
1523  return false;
1524  }
1525 
1526  wxString name;
1527  if (info.flags & kAudioUnitParameterFlag_HasCFNameString)
1528  {
1529  name = wxCFStringRef::AsString(info.cfNameString);
1530  }
1531 
1532  if (name.IsEmpty())
1533  {
1534  continue;
1535  }
1536 
1537  AudioUnitParameterValue value;
1538  result = AudioUnitGetParameter(mUnit,
1539  array[i],
1540  kAudioUnitScope_Global,
1541  0,
1542  &value);
1543  if (result != noErr)
1544  {
1545  return false;
1546  }
1547  parms.Write(name, value);
1548  }
1549 
1550  return true;
1551 }
1552 
1553 bool AudioUnitEffect::SetAutomationParameters(EffectAutomationParameters & parms)
1554 {
1555  OSStatus result;
1556  UInt32 dataSize;
1557  Boolean isWritable;
1558 
1559  result = AudioUnitGetPropertyInfo(mUnit,
1560  kAudioUnitProperty_ParameterList,
1561  kAudioUnitScope_Global,
1562  0,
1563  &dataSize,
1564  &isWritable);
1565  if (result != noErr)
1566  {
1567  return false;
1568  }
1569 
1570  UInt32 cnt = dataSize / sizeof(AudioUnitParameterID);
1571  ArrayOf<AudioUnitParameterID> array {cnt};
1572 
1573  result = AudioUnitGetProperty(mUnit,
1574  kAudioUnitProperty_ParameterList,
1575  kAudioUnitScope_Global,
1576  0,
1577  array.get(),
1578  &dataSize);
1579  if (result != noErr)
1580  {
1581  return false;
1582  }
1583 
1584  for (int i = 0; i < cnt; i++)
1585  {
1586  AudioUnitParameterInfoEx info;
1587  dataSize = sizeof(info);
1588  result = AudioUnitGetProperty(mUnit,
1589  kAudioUnitProperty_ParameterInfo,
1590  kAudioUnitScope_Global,
1591  array[i],
1592  &info,
1593  &dataSize);
1594  if (result != noErr)
1595  {
1596  return false;
1597  }
1598 
1599  wxString name;
1600  if (info.flags & kAudioUnitParameterFlag_HasCFNameString)
1601  {
1602  name = wxCFStringRef::AsString(info.cfNameString);
1603  }
1604 
1605  if (name.IsEmpty())
1606  {
1607  continue;
1608  }
1609 
1610 
1611  double d = 0.0;
1612  if (!parms.Read(name, &d))
1613  {
1614  return false;
1615  }
1616 
1617  AudioUnitParameterValue value = d;
1618  result = AudioUnitSetParameter(mUnit,
1619  array[i],
1620  kAudioUnitScope_Global,
1621  0,
1622  value,
1623  0);
1624  if (result != noErr)
1625  {
1626  return false;
1627  }
1628  }
1629 
1630  AudioUnitParameter aup;
1631  aup.mAudioUnit = mUnit;
1632  aup.mParameterID = kAUParameterListener_AnyParameter;
1633  aup.mScope = kAudioUnitScope_Global;
1634  aup.mElement = 0;
1635  AUParameterListenerNotify(NULL, NULL, &aup);
1636 
1637  return true;
1638 }
1639 
1640 bool AudioUnitEffect::LoadUserPreset(const wxString & name)
1641 {
1642  return LoadParameters(name);
1643 }
1644 
1645 bool AudioUnitEffect::SaveUserPreset(const wxString & name)
1646 {
1647  return SaveParameters(name);
1648 }
1649 
1650 bool AudioUnitEffect::LoadFactoryPreset(int id)
1651 {
1652  OSStatus result;
1653 
1654  // Retrieve the list of factory presets
1655  CFArrayRef array{};
1656  UInt32 dataSize = sizeof(CFArrayRef);
1657  result = AudioUnitGetProperty(mUnit,
1658  kAudioUnitProperty_FactoryPresets,
1659  kAudioUnitScope_Global,
1660  0,
1661  &array,
1662  &dataSize);
1663  CFunique_ptr < const __CFArray > uarray { array };
1664  if (result != noErr)
1665  {
1666  return false;
1667  }
1668 
1669  if (id < 0 || id >= CFArrayGetCount(array))
1670  {
1671  return false;
1672  }
1673 
1674  AUPreset *preset = (AUPreset *) CFArrayGetValueAtIndex(array, id);
1675 
1676  result = AudioUnitSetProperty(mUnit,
1677  kAudioUnitProperty_PresentPreset,
1678  kAudioUnitScope_Global,
1679  0,
1680  preset,
1681  sizeof(AUPreset));
1682  if (result == noErr)
1683  {
1684  AudioUnitParameter aup;
1685  aup.mAudioUnit = mUnit;
1686  aup.mParameterID = kAUParameterListener_AnyParameter;
1687  aup.mScope = kAudioUnitScope_Global;
1688  aup.mElement = 0;
1689  AUParameterListenerNotify(NULL, NULL, &aup);
1690  }
1691 
1692  return result == noErr;
1693 }
1694 
1695 bool AudioUnitEffect::LoadFactoryDefaults()
1696 {
1697  return LoadParameters(mHost->GetFactoryDefaultsGroup());
1698 }
1699 
1700 wxArrayString AudioUnitEffect::GetFactoryPresets()
1701 {
1702  OSStatus result;
1703  wxArrayString presets;
1704 
1705  // Retrieve the list of factory presets
1706  CFArrayRef array{};
1707  UInt32 dataSize = sizeof(CFArrayRef);
1708  result = AudioUnitGetProperty(mUnit,
1709  kAudioUnitProperty_FactoryPresets,
1710  kAudioUnitScope_Global,
1711  0,
1712  &array,
1713  &dataSize);
1714  CFunique_ptr< const __CFArray > uarray { array };
1715  if (result == noErr)
1716  {
1717  for (CFIndex i = 0, cnt = CFArrayGetCount(array); i < cnt; i++)
1718  {
1719  AUPreset *preset = (AUPreset *) CFArrayGetValueAtIndex(array, i);
1720  presets.Add(wxCFStringRef::AsString(preset->presetName));
1721  }
1722  }
1723 
1724  return presets;
1725 }
1726 
1727 // ============================================================================
1728 // EffectUIClientInterface Implementation
1729 // ============================================================================
1730 
1731 void AudioUnitEffect::SetHostUI(EffectUIHostInterface *host)
1732 {
1733  mUIHost = host;
1734 }
1735 
1736 bool AudioUnitEffect::PopulateUI(wxWindow *parent)
1737 {
1738  // OSStatus result;
1739 
1740  mDialog = static_cast<wxDialog *>(wxGetTopLevelParent(parent));
1741  mParent = parent;
1742 
1743  wxPanel *container;
1744  {
1745  auto mainSizer = std::make_unique<wxBoxSizer>(wxVERTICAL);
1746 
1747  wxASSERT(mParent); // To justify safenew
1748  container = safenew wxPanelWrapper(mParent, wxID_ANY);
1749  mainSizer->Add(container, 1, wxEXPAND);
1750 
1751  mParent->SetSizer(mainSizer.release());
1752  }
1753 
1754  if (mUIType == wxT("Plain"))
1755  {
1756  if (!CreatePlain(mParent))
1757  {
1758  return false;
1759  }
1760  }
1761  else
1762  {
1763  auto pControl = Destroy_ptr<AUControl>( safenew AUControl );
1764  if (!pControl)
1765  {
1766  return false;
1767  }
1768 
1769  if (!pControl->Create(container, mComponent, mUnit, mUIType == wxT("Full")))
1770  {
1771  return false;
1772  }
1773 
1774  {
1775  auto innerSizer = std::make_unique<wxBoxSizer>(wxVERTICAL);
1776 
1777  innerSizer->Add((mpControl = pControl.release()), 1, wxEXPAND);
1778  container->SetSizer(innerSizer.release());
1779  }
1780 
1781  mParent->SetMinSize(wxDefaultSize);
1782 
1783 #ifdef __WXMAC__
1784 #ifdef __WX_EVTLOOP_BUSY_WAITING__
1785  wxEventLoop::SetBusyWaiting(true);
1786 #endif
1787 #endif
1788  }
1789 
1790  mParent->PushEventHandler(this);
1791 
1792  return true;
1793 }
1794 
1795 bool AudioUnitEffect::IsGraphicalUI()
1796 {
1797  return mUIType != wxT("Plain");
1798 }
1799 
1800 bool AudioUnitEffect::ValidateUI()
1801 {
1802 #if 0
1803  if (!mParent->Validate())
1804  {
1805  return false;
1806  }
1807 
1808  if (GetType() == EffectTypeGenerate)
1809  {
1810  mHost->SetDuration(mDuration->GetValue());
1811  }
1812 #endif
1813  return true;
1814 }
1815 
1816 bool AudioUnitEffect::CreatePlain(wxWindow *parent)
1817 {
1818  return false;
1819 }
1820 
1821 bool AudioUnitEffect::HideUI()
1822 {
1823 #if 0
1824  if (GetType() == EffectTypeAnalyze || mNumOutputControls > 0)
1825  {
1826  return false;
1827  }
1828 #endif
1829  return true;
1830 }
1831 
1832 bool AudioUnitEffect::CloseUI()
1833 {
1834 #ifdef __WXMAC__
1835 #ifdef __WX_EVTLOOP_BUSY_WAITING__
1836  wxEventLoop::SetBusyWaiting(false);
1837 #endif
1838  if (mpControl)
1839  mpControl->Close(), mpControl = nullptr;
1840 #endif
1841 
1842  mParent->RemoveEventHandler(this);
1843 
1844  mUIHost = NULL;
1845  mParent = NULL;
1846  mDialog = NULL;
1847 
1848  return true;
1849 }
1850 
1851 bool AudioUnitEffect::CanExportPresets()
1852 {
1853  return true;
1854 }
1855 
1856 void AudioUnitEffect::ExportPresets()
1857 {
1858  AudioUnitEffectExportDialog dlg(mDialog, this);
1859  dlg.ShowModal();
1860 }
1861 
1862 void AudioUnitEffect::ImportPresets()
1863 {
1864  AudioUnitEffectImportDialog dlg(mDialog, this);
1865  dlg.ShowModal();
1866 }
1867 
1868 bool AudioUnitEffect::HasOptions()
1869 {
1870  return true;
1871 }
1872 
1873 void AudioUnitEffect::ShowOptions()
1874 {
1875  AudioUnitEffectOptionsDialog dlg(mParent, mHost);
1876  if (dlg.ShowModal())
1877  {
1878  // Reinitialize configuration settings
1879  mHost->GetSharedConfig(wxT("Options"), wxT("UseLatency"), mUseLatency, true);
1880  mHost->GetSharedConfig(wxT("Options"), wxT("UIType"), mUIType, wxT("Full"));
1881 
1882  mUIType = wxGetTranslation(mUIType);
1883  }
1884 }
1885 
1886 // ============================================================================
1887 // AudioUnitEffect Implementation
1888 // ============================================================================
1889 
1890 bool AudioUnitEffect::LoadParameters(const wxString & group)
1891 {
1892  wxString parms;
1893  if (!mHost->GetPrivateConfig(group, wxT("Parameters"), parms, wxEmptyString))
1894  {
1895  return false;
1896  }
1897 
1898  EffectAutomationParameters eap;
1899  if (!eap.SetParameters(parms))
1900  {
1901  return false;
1902  }
1903 
1904  return SetAutomationParameters(eap);
1905 }
1906 
1907 bool AudioUnitEffect::SaveParameters(const wxString & group)
1908 {
1909  EffectAutomationParameters eap;
1910  if (!GetAutomationParameters(eap))
1911  {
1912  return false;
1913  }
1914 
1915  wxString parms;
1916  if (!eap.GetParameters(parms))
1917  {
1918  return false;
1919  }
1920 
1921  return mHost->SetPrivateConfig(group, wxT("Parameters"), parms);
1922 }
1923 
1924 bool AudioUnitEffect::SetRateAndChannels()
1925 {
1926  OSStatus result;
1927 
1928  if (mUnitInitialized)
1929  {
1930  AudioUnitUninitialize(mUnit);
1931 
1932  mUnitInitialized = false;
1933  }
1934 
1935  AudioStreamBasicDescription streamFormat {
1936  // Float64 mSampleRate;
1937  mSampleRate,
1938 
1939  // UInt32 mFormatID;
1940  kAudioFormatLinearPCM,
1941 
1942  // UInt32 mFormatFlags;
1943  (kAudioFormatFlagsNativeFloatPacked |
1944  kAudioFormatFlagIsNonInterleaved),
1945 
1946  // UInt32 mBytesPerPacket;
1947  sizeof(float),
1948 
1949  // UInt32 mFramesPerPacket;
1950  1,
1951 
1952  // UInt32 mBytesPerFrame;
1953  sizeof(float),
1954 
1955  // UInt32 mChannelsPerFrame;
1956  mAudioIns,
1957 
1958  // UInt32 mBitsPerChannel;
1959  sizeof(float) * 8,
1960 
1961  // UInt32 mReserved;
1962  0
1963  };
1964 
1965  result = AudioUnitSetProperty(mUnit,
1966  kAudioUnitProperty_SampleRate,
1967  kAudioUnitScope_Global,
1968  0,
1969  &mSampleRate,
1970  sizeof(Float64));
1971  if (result != noErr)
1972  {
1973  wxPrintf("%ls Didn't accept sample rate on global\n", GetName().wx_str());
1974  return false;
1975  }
1976 
1977  if (mAudioIns > 0)
1978  {
1979  result = AudioUnitSetProperty(mUnit,
1980  kAudioUnitProperty_SampleRate,
1981  kAudioUnitScope_Input,
1982  0,
1983  &mSampleRate,
1984  sizeof(Float64));
1985  if (result != noErr)
1986  {
1987  wxPrintf("%ls Didn't accept sample rate on input\n", GetName().wx_str());
1988  return false;
1989  }
1990 
1991  result = AudioUnitSetProperty(mUnit,
1992  kAudioUnitProperty_StreamFormat,
1993  kAudioUnitScope_Input,
1994  0,
1995  &streamFormat,
1996  sizeof(AudioStreamBasicDescription));
1997  if (result != noErr)
1998  {
1999  wxPrintf("%ls didn't accept stream format on input\n", GetName().wx_str());
2000  return false;
2001  }
2002  }
2003 
2004  if (mAudioOuts > 0)
2005  {
2006  result = AudioUnitSetProperty(mUnit,
2007  kAudioUnitProperty_SampleRate,
2008  kAudioUnitScope_Output,
2009  0,
2010  &mSampleRate,
2011  sizeof(Float64));
2012  if (result != noErr)
2013  {
2014  wxPrintf("%ls Didn't accept sample rate on output\n", GetName().wx_str());
2015  return false;
2016  }
2017 
2018  streamFormat.mChannelsPerFrame = mAudioOuts;
2019  result = AudioUnitSetProperty(mUnit,
2020  kAudioUnitProperty_StreamFormat,
2021  kAudioUnitScope_Output,
2022  0,
2023  &streamFormat,
2024  sizeof(AudioStreamBasicDescription));
2025 
2026  if (result != noErr)
2027  {
2028  wxPrintf("%ls didn't accept stream format on output\n", GetName().wx_str());
2029  return false;
2030  }
2031  }
2032 
2033  result = AudioUnitInitialize(mUnit);
2034  if (result != noErr)
2035  {
2036  wxPrintf("Couldn't initialize audio unit\n");
2037  return false;
2038  }
2039 
2040  mUnitInitialized = true;
2041 
2042  return true;
2043 }
2044 
2045 bool AudioUnitEffect::CopyParameters(AudioUnit srcUnit, AudioUnit dstUnit)
2046 {
2047  OSStatus result;
2048  Float32 parameterValue;
2049  UInt32 size;
2050 
2051  // Get number of parameters by passing NULL in the data field and
2052  // getting back the size of the parameter list
2053 
2054  size = 0;
2055  result = AudioUnitGetProperty(srcUnit,
2056  kAudioUnitProperty_ParameterList,
2057  kAudioUnitScope_Global,
2058  0,
2059  NULL,
2060  &size);
2061  if (result != 0)
2062  {
2063  wxPrintf("Couldn't get number of parameters\n");
2064  return false;
2065  }
2066 
2067  // Now get the list of all parameter IDs
2068 
2069  auto numParameters = size / sizeof(AudioUnitParameterID);
2070  ArrayOf<AudioUnitParameterID> parameters{ numParameters };
2071  result = AudioUnitGetProperty(srcUnit,
2072  kAudioUnitProperty_ParameterList,
2073  kAudioUnitScope_Global,
2074  0,
2075  parameters.get(),
2076  &size);
2077  if (result != 0)
2078  {
2079  wxPrintf("Couldn't get parameter list\n");
2080  return false;
2081  }
2082 
2083  // Copy the parameters from the main unit to the unit specific to
2084  // this track
2085 
2086  for (unsigned i = 0; i < numParameters; i++)
2087  {
2088  result = AudioUnitGetParameter(srcUnit,
2089  parameters[i],
2090  kAudioUnitScope_Global,
2091  0,
2092  &parameterValue);
2093  if (result != 0)
2094  {
2095  wxPrintf("Couldn't get parameter %d: ID=%d\n", i, (int)parameters[i]);
2096  continue;
2097  }
2098 
2099  result = AudioUnitSetParameter(dstUnit,
2100  parameters[i],
2101  kAudioUnitScope_Global,
2102  0,
2103  parameterValue,
2104  0);
2105  if (result != 0)
2106  {
2107  wxPrintf("Couldn't set parameter %d: ID=%d\n", i, (int)parameters[i]);
2108  }
2109  }
2110 
2111  return true;
2112 }
2113 
2114 unsigned AudioUnitEffect::GetChannelCount()
2115 {
2116  return mNumChannels;
2117 }
2118 
2119 void AudioUnitEffect::SetChannelCount(unsigned numChannels)
2120 {
2121  mNumChannels = numChannels;
2122 }
2123 
2124 OSStatus AudioUnitEffect::Render(AudioUnitRenderActionFlags *inActionFlags,
2125  const AudioTimeStamp *inTimeStamp,
2126  UInt32 inBusNumber,
2127  UInt32 inNumFrames,
2128  AudioBufferList *ioData)
2129 {
2130  for (int i = 0; i < ioData->mNumberBuffers; i++)
2131  ioData->mBuffers[i].mData = mInputList[0].mBuffers[i].mData;
2132 
2133  return 0;
2134 }
2135 
2136 // static
2137 OSStatus AudioUnitEffect::RenderCallback(void *inRefCon,
2138  AudioUnitRenderActionFlags *inActionFlags,
2139  const AudioTimeStamp *inTimeStamp,
2140  UInt32 inBusNumber,
2141  UInt32 inNumFrames,
2142  AudioBufferList *ioData)
2143 {
2144  return ((AudioUnitEffect *) inRefCon)->Render(inActionFlags,
2145  inTimeStamp,
2146  inBusNumber,
2147  inNumFrames,
2148  ioData);
2149 }
2150 
2151 void AudioUnitEffect::EventListener(const AudioUnitEvent *inEvent,
2152  AudioUnitParameterValue inParameterValue)
2153 {
2154  // Handle property changes
2155  if (inEvent->mEventType == kAudioUnitEvent_PropertyChange)
2156  {
2157  // We're only registered for Latency changes
2158  if (inEvent->mArgument.mProperty.mPropertyID == kAudioUnitProperty_Latency)
2159  {
2160  // Allow change to be used
2161  //mLatencyDone = false;
2162  }
2163 
2164  return;
2165  }
2166 
2167  // Only parameter changes at this point
2168 
2169  if (mMaster)
2170  {
2171  // We're a slave, so just set the parameter
2172  AudioUnitSetParameter(mUnit,
2173  inEvent->mArgument.mParameter.mParameterID,
2174  kAudioUnitScope_Global,
2175  0,
2176  inParameterValue,
2177  0);
2178  }
2179  else
2180  {
2181  // We're the master, so propogate
2182  for (size_t i = 0, cnt = mSlaves.size(); i < cnt; i++)
2183  mSlaves[i]->EventListener(inEvent, inParameterValue);
2184  }
2185 }
2186 
2187 // static
2188 void AudioUnitEffect::EventListenerCallback(void *inCallbackRefCon,
2189  void *inObject,
2190  const AudioUnitEvent *inEvent,
2191  UInt64 inEventHostTime,
2192  AudioUnitParameterValue inParameterValue)
2193 {
2194  ((AudioUnitEffect *) inCallbackRefCon)->EventListener(inEvent,
2195  inParameterValue);
2196 }
2197 
2198 void AudioUnitEffect::GetChannelCounts()
2199 {
2200  Boolean isWritable = 0;
2201  UInt32 dataSize = 0;
2202  OSStatus result;
2203 
2204  // Does AU have channel info
2205  result = AudioUnitGetPropertyInfo(mUnit,
2206  kAudioUnitProperty_SupportedNumChannels,
2207  kAudioUnitScope_Global,
2208  0,
2209  &dataSize,
2210  &isWritable);
2211  if (result)
2212  {
2213  // None supplied. Apparently all FX type units can do any number of INs
2214  // and OUTs as long as they are the same number. In this case, we'll
2215  // just say stereo.
2216  //
2217  // We should probably check to make sure we're dealing with an FX type.
2218  mAudioIns = 2;
2219  mAudioOuts = 2;
2220  return;
2221  }
2222 
2223  ArrayOf<char> buffer{ dataSize };
2224  auto info = (AUChannelInfo *) buffer.get();
2225 
2226  // Retrieve the channel info
2227  result = AudioUnitGetProperty(mUnit,
2228  kAudioUnitProperty_SupportedNumChannels,
2229  kAudioUnitScope_Global,
2230  0,
2231  info,
2232  &dataSize);
2233  if (result)
2234  {
2235  // Oh well, not much we can do out this case
2236  mAudioIns = 2;
2237  mAudioOuts = 2;
2238 
2239  return;
2240  }
2241 
2242  // This is where it gets weird...not sure what is the best
2243  // way to do this really. If we knew how many ins/outs we
2244  // really needed, we could make a better choice.
2245 
2246  bool haven2m = false; // nothing -> mono
2247  bool haven2s = false; // nothing -> stereo
2248  bool havem2n = false; // mono -> nothing
2249  bool haves2n = false; // stereo -> nothing
2250  bool havem2m = false; // mono -> mono
2251  bool haves2s = false; // stereo -> stereo
2252  bool havem2s = false; // mono -> stereo
2253  bool haves2m = false; // stereo -> mono
2254 
2255  mAudioIns = 2;
2256  mAudioOuts = 2;
2257 
2258  // Look only for exact channel constraints
2259  for (int i = 0; i < dataSize / sizeof(AUChannelInfo); i++)
2260  {
2261  AUChannelInfo *ci = &info[i];
2262 
2263  int ic = ci->inChannels;
2264  int oc = ci->outChannels;
2265 
2266  if (ic < 0 && oc >= 0)
2267  {
2268  ic = 2;
2269  }
2270  else if (ic >= 0 && oc < 0)
2271  {
2272  oc = 2;
2273  }
2274  else if (ic < 0 && oc < 0)
2275  {
2276  ic = 2;
2277  oc = 2;
2278  }
2279 
2280  if (ic == 2 && oc == 2)
2281  {
2282  haves2s = true;
2283  }
2284  else if (ic == 1 && oc == 1)
2285  {
2286  havem2m = true;
2287  }
2288  else if (ic == 1 && oc == 2)
2289  {
2290  havem2s = true;
2291  }
2292  else if (ic == 2 && oc == 1)
2293  {
2294  haves2m = true;
2295  }
2296  else if (ic == 0 && oc == 2)
2297  {
2298  haven2s = true;
2299  }
2300  else if (ic == 0 && oc == 1)
2301  {
2302  haven2m = true;
2303  }
2304  else if (ic == 1 && oc == 0)
2305  {
2306  havem2n = true;
2307  }
2308  else if (ic == 2 && oc == 0)
2309  {
2310  haves2n = true;
2311  }
2312  }
2313 
2314  if (haves2s)
2315  {
2316  mAudioIns = 2;
2317  mAudioOuts = 2;
2318  }
2319  else if (havem2m)
2320  {
2321  mAudioIns = 1;
2322  mAudioOuts = 1;
2323  }
2324  else if (havem2s)
2325  {
2326  mAudioIns = 1;
2327  mAudioOuts = 2;
2328  }
2329  else if (haves2m)
2330  {
2331  mAudioIns = 2;
2332  mAudioOuts = 1;
2333  }
2334  else if (haven2m)
2335  {
2336  mAudioIns = 0;
2337  mAudioOuts = 1;
2338  }
2339  else if (haven2s)
2340  {
2341  mAudioIns = 0;
2342  mAudioOuts = 2;
2343  }
2344  else if (haves2n)
2345  {
2346  mAudioIns = 2;
2347  mAudioOuts = 0;
2348  }
2349  else if (havem2n)
2350  {
2351  mAudioIns = 1;
2352  mAudioOuts = 0;
2353  }
2354 
2355  return;
2356 }
2357 
2358 #endif
wxChoice * TieChoice(const wxString &Prompt, WrappedType &WrappedRef, const wxArrayString *pChoices)
Derived from ShuttleGuiBase, an Audacity specific class for shuttling data to and from GUI...
Definition: ShuttleGui.h:366
const wxChar * desc
Definition: ExportPCM.cpp:58
DECLARE_BUILTIN_MODULE(LadspaBuiltin)
Base class for many of the effects in Audacity.
Definition: Effect.h:61
#define XO(s)
Definition: Internat.h:30
Main class to control the export function.
#define safenew
Definition: Audacity.h:223
void EndHorizontalLay()
Definition: ShuttleGui.cpp:975
void EndVerticalLay()
Definition: ShuttleGui.cpp:991
void StartHorizontalLay(int PositionFlags=wxALIGN_CENTRE, int iProp=1)
Definition: ShuttleGui.cpp:966
wxListCtrl * AddListControlReportMode()
Definition: ShuttleGui.cpp:627
void SetStyle(int Style)
Definition: ShuttleGui.h:252
DECLARE_MODULE_ENTRY(AudacityModule)
EVT_BUTTON(wxID_NO, DependencyDialog::OnNo) EVT_BUTTON(wxID_YES
_("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 &)
ValueRestorer< T > valueRestorer(T &var)
Definition: MemoryX.h:875
wxCheckBox * TieCheckBox(const wxString &Prompt, WrappedType &WrappedRef)
const wxChar * name
Definition: Distortion.cpp:94
Memory.h template class for making an array of float, bool, etc.
Definition: MemoryX.h:469
wxStaticText * AddVariableText(const wxString &Str, bool bCenter=false, int PositionFlags=0)
Definition: ShuttleGui.cpp:373
wxStaticBox * StartStatic(const wxString &Str, int iProp=0)
Definition: ShuttleGui.cpp:701
An Effect class that handles a wide range of effects. ??Mac only??
std::unique_ptr< T, Destroyer< T >> Destroy_ptr
Definition: MemoryX.h:814
void AddStandardButtons(long buttons=eOkButton|eCancelButton, wxButton *extra=NULL)
END_EVENT_TABLE()
void SetBorder(int Border)
Definition: ShuttleGui.h:251
a wxControl with Cocoa/Carbon support
Definition: AUControl.h:37
void StartVerticalLay(int iProp=1)
Definition: ShuttleGui.cpp:982