Audacity  2.2.2
LV2Effect.cpp
Go to the documentation of this file.
1 /**********************************************************************
2 
3  Audacity: A Digital Audio Editor
4 
5  LV2Effect.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 #if defined(USE_LV2)
15 
16 #include <cmath>
17 
18 #include <wx/button.h>
19 #include <wx/choice.h>
20 #include <wx/dcbuffer.h>
21 #include <wx/dialog.h>
22 
23 #ifdef __WXMAC__
24 #include <wx/evtloop.h>
25 #endif
26 
27 #include <wx/sizer.h>
28 #include <wx/statbox.h>
29 #include <wx/tokenzr.h>
30 #include <wx/intl.h>
31 #include <wx/scrolwin.h>
32 
33 #include "LoadLV2.h"
34 #include "LV2Effect.h"
35 #include "../../Internat.h"
36 #include "../../ShuttleGui.h"
37 #include "../../widgets/valnum.h"
38 #include "../../widgets/wxPanelWrapper.h"
39 #include "../../widgets/ErrorDialog.h"
40 
41 #include "lilv/lilv.h"
42 #include "suil/suil.h"
43 #include "lv2/lv2plug.in/ns/ext/instance-access/instance-access.h"
44 #include "lv2/lv2plug.in/ns/ext/port-groups/port-groups.h"
45 #include "lv2/lv2plug.in/ns/ext/buf-size/buf-size.h"
46 #include "lv2/lv2plug.in/ns/ext/parameters/parameters.h"
47 #include "lv2/lv2plug.in/ns/extensions/ui/ui.h"
48 
49 #if defined(__WXGTK__)
50 #include <gtk/gtk.h>
51 #include "win_gtk.h"
52 #endif
53 
54 #if defined(__WXMSW__)
55 #include <wx/msw/wrapwin.h>
56 #endif
57 
58 #if defined(__WXMAC__)
59 #include <AppKit/AppKit.h>
60 #endif
61 
62 // Define the static URI nodes
63 #undef URI
64 #define URI(n, u) LilvNode *LV2Effect::n = NULL;
65 URILIST
66 
68 //
69 // LV2EffectMeter
70 //
72 
73 class LV2EffectMeter final : public wxWindow
74 {
75 public:
76  LV2EffectMeter(wxWindow *parent, const LV2Port & ctrl);
77  virtual ~LV2EffectMeter();
78 
79 private:
80  void OnErase(wxEraseEvent & evt);
81  void OnPaint(wxPaintEvent & evt);
82  void OnIdle(wxIdleEvent & evt);
83  void OnSize(wxSizeEvent & evt);
84 
85 private:
86  const LV2Port & mCtrl;
87  float mLastValue;
88 
89  DECLARE_EVENT_TABLE()
90 };
91 
92 BEGIN_EVENT_TABLE(LV2EffectMeter, wxWindow)
93  EVT_IDLE(LV2EffectMeter::OnIdle)
94  EVT_ERASE_BACKGROUND(LV2EffectMeter::OnErase)
95  EVT_PAINT(LV2EffectMeter::OnPaint)
96  EVT_SIZE(LV2EffectMeter::OnSize)
98 
99 LV2EffectMeter::LV2EffectMeter(wxWindow *parent, const LV2Port & ctrl)
100 : wxWindow(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxDEFAULT_CONTROL_BORDER),
101  mCtrl(ctrl)
102 {
103  mLastValue = -mCtrl.mVal;
104 
105  SetBackgroundColour(*wxWHITE);
106 }
107 
108 LV2EffectMeter::~LV2EffectMeter()
109 {
110 }
111 
112 void LV2EffectMeter::OnIdle(wxIdleEvent & WXUNUSED(evt))
113 {
114  if (mLastValue != mCtrl.mVal)
115  {
116  Refresh(false);
117  }
118 }
119 
120 void LV2EffectMeter::OnErase(wxEraseEvent & WXUNUSED(evt))
121 {
122  // Just ignore it to prevent flashing
123 }
124 
125 void LV2EffectMeter::OnPaint(wxPaintEvent & WXUNUSED(evt))
126 {
127  std::unique_ptr<wxDC> dc{ wxAutoBufferedPaintDCFactory(this) };
128 
129  // Cache some metrics
130  wxRect r = GetClientRect();
131  wxCoord x = r.GetLeft();
132  wxCoord y = r.GetTop();
133  wxCoord w = r.GetWidth();
134  wxCoord h = r.GetHeight();
135 
136  // These use unscaled value, min, and max
137  float val = mCtrl.mVal;
138  if (val > mCtrl.mMax)
139  {
140  val = mCtrl.mMax;
141  }
142  if (val < mCtrl.mMin)
143  {
144  val = mCtrl.mMin;
145  }
146  val -= mCtrl.mMin;
147 
148  // Setup for erasing the background
149  dc->SetPen(*wxTRANSPARENT_PEN);
150  dc->SetBrush(wxColour(100, 100, 220));
151 
152  dc->Clear();
153  dc->DrawRectangle(x, y, (w * (val / fabs(mCtrl.mMax - mCtrl.mMin))), h);
154 
155  mLastValue = mCtrl.mVal;
156 }
157 
158 void LV2EffectMeter::OnSize(wxSizeEvent & WXUNUSED(evt))
159 {
160  Refresh(false);
161 }
162 
164 //
165 // LV2EffectSettingsDialog
166 //
168 
169 class LV2EffectSettingsDialog final : public wxDialogWrapper
170 {
171 public:
172  LV2EffectSettingsDialog(wxWindow *parent, LV2Effect *effect);
173  virtual ~LV2EffectSettingsDialog();
174 
175  void PopulateOrExchange(ShuttleGui & S);
176 
177  void OnOk(wxCommandEvent & evt);
178 
179 private:
180  LV2Effect *mEffect;
181  bool mUseLatency;
182  bool mUseGUI;
183 
184  DECLARE_EVENT_TABLE()
185 };
186 
187 BEGIN_EVENT_TABLE(LV2EffectSettingsDialog, wxDialogWrapper)
188  EVT_BUTTON(wxID_OK, LV2EffectSettingsDialog::OnOk)
190 
191 LV2EffectSettingsDialog::LV2EffectSettingsDialog(wxWindow *parent, LV2Effect *effect)
192 : wxDialogWrapper(parent, wxID_ANY, wxString(_("LV2 Effect Settings")))
193 {
194  mEffect = effect;
195 
196  mEffect->mHost->GetSharedConfig(wxT("Settings"), wxT("UseLatency"), mUseLatency, true);
197  mEffect->mHost->GetSharedConfig(wxT("Settings"), wxT("UseGUI"), mUseGUI, true);
198 
199  ShuttleGui S(this, eIsCreating);
200  PopulateOrExchange(S);
201 }
202 
203 LV2EffectSettingsDialog::~LV2EffectSettingsDialog()
204 {
205 }
206 
207 void LV2EffectSettingsDialog::PopulateOrExchange(ShuttleGui & S)
208 {
209  S.SetBorder(5);
210  S.StartHorizontalLay(wxEXPAND, 1);
211  {
212  S.StartVerticalLay(false);
213  {
214  S.StartStatic(_("Latency Compensation"));
215  {
216  S.AddVariableText(wxString() +
217  _("As part of their processing, some LV2 effects must delay returning ") +
218  _("audio to Audacity. When not compensating for this delay, you will ") +
219  _("notice that small silences have been inserted into the audio. ") +
220  _("Enabling this setting will provide that compensation, but it may ") +
221  _("not work for all LV2 effects."))->Wrap(650);
222 
223  S.StartHorizontalLay(wxALIGN_LEFT);
224  {
225  S.TieCheckBox(_("Enable &compensation"),
226  mUseLatency);
227  }
228  S.EndHorizontalLay();
229  }
230  S.EndStatic();
231 
232  S.StartStatic(_("Graphical Mode"));
233  {
234  S.AddVariableText(wxString() +
235  _("LV2 effects can have a graphical interface for setting parameter values.") +
236  _(" A basic text-only method is also available. ") +
237  _(" Reopen the effect for this to take effect."))->Wrap(650);
238  S.TieCheckBox(_("Enable &graphical interface"),
239  mUseGUI);
240  }
241  S.EndStatic();
242  }
243  S.EndVerticalLay();
244  }
245  S.EndHorizontalLay();
246 
247  S.AddStandardButtons();
248 
249  Layout();
250  Fit();
251  Center();
252 }
253 
254 void LV2EffectSettingsDialog::OnOk(wxCommandEvent & WXUNUSED(evt))
255 {
256  if (!Validate())
257  {
258  return;
259  }
260 
262  PopulateOrExchange(S);
263 
264  mEffect->mHost->SetSharedConfig(wxT("Settings"), wxT("UseLatency"), mUseLatency);
265  mEffect->mHost->SetSharedConfig(wxT("Settings"), wxT("UseGUI"), mUseGUI);
266 
267  EndModal(wxID_OK);
268 }
269 
271 //
272 // LV2Effect
273 //
275 
276 enum
277 {
278  ID_Duration = 10000,
279  ID_Triggers = 11000,
280  ID_Toggles = 12000,
281  ID_Sliders = 13000,
282  ID_Choices = 14000,
283  ID_Texts = 15000,
284 };
285 
286 BEGIN_EVENT_TABLE(LV2Effect, wxEvtHandler)
287  EVT_COMMAND_RANGE(ID_Triggers, ID_Triggers + 999, wxEVT_COMMAND_BUTTON_CLICKED, LV2Effect::OnTrigger)
288  EVT_COMMAND_RANGE(ID_Toggles, ID_Toggles + 999, wxEVT_COMMAND_CHECKBOX_CLICKED, LV2Effect::OnToggle)
289  EVT_COMMAND_RANGE(ID_Sliders, ID_Sliders + 999, wxEVT_COMMAND_SLIDER_UPDATED, LV2Effect::OnSlider)
290  EVT_COMMAND_RANGE(ID_Choices, ID_Choices + 999, wxEVT_COMMAND_CHOICE_SELECTED, LV2Effect::OnChoice)
291  EVT_COMMAND_RANGE(ID_Texts, ID_Texts + 999, wxEVT_COMMAND_TEXT_UPDATED, LV2Effect::OnText)
292 
293  EVT_IDLE(LV2Effect::OnIdle)
295 
296 LV2Effect::LV2Effect(const LilvPlugin *plug)
297 {
298  mPlug = plug;
299 
300  mHost = NULL;
301  mMaster = NULL;
302  mProcess = NULL;
303  mSuilInstance = NULL;
304 
305  mSampleRate = 44100;
306  mBlockSize = 512;
307 
308  mLatencyPort = -1;
309  mLatencyDone = false;
310  mLatency = 0.0;
311 
312  mDialog = NULL;
313 
314  mIdleFeature = NULL;
315  mOptionsInterface = NULL;
316 
317  mFactoryPresetsLoaded = false;
318 }
319 
320 LV2Effect::~LV2Effect()
321 {
322 }
323 
324 // ============================================================================
325 // IdentInterface Implementation
326 // ============================================================================
327 
328 wxString LV2Effect::GetPath()
329 {
330  return LilvString(lilv_plugin_get_uri(mPlug));
331 }
332 
333 wxString LV2Effect::GetSymbol()
334 {
335  return LilvString(lilv_plugin_get_name(mPlug), true);
336 }
337 
338 wxString LV2Effect::GetName()
339 {
340  return GetSymbol();
341 }
342 
343 wxString LV2Effect::GetVendor()
344 {
345  wxString vendor = LilvString(lilv_plugin_get_author_name(mPlug), true);
346 
347  if (vendor.IsEmpty())
348  {
349  vendor = XO("n/a");
350  }
351 
352  return vendor;
353 }
354 
355 wxString LV2Effect::GetVersion()
356 {
357  return wxT("1.0");
358 }
359 
360 wxString LV2Effect::GetDescription()
361 {
362  return _("n/a");
363 }
364 
365 // ============================================================================
366 // EffectIdentInterface Implementation
367 // ============================================================================
368 
369 EffectType LV2Effect::GetType()
370 {
371  if (GetAudioInCount() == 0 && GetAudioOutCount() == 0)
372  {
373  return EffectTypeNone;
374  }
375 
376  if (GetAudioInCount() == 0)
377  {
378  return EffectTypeGenerate;
379  }
380 
381  if (GetAudioOutCount() == 0)
382  {
383  return EffectTypeAnalyze;
384  }
385 
386  return EffectTypeProcess;
387 }
388 
389 wxString LV2Effect::GetFamilyId()
390 {
391  return LV2EFFECTS_FAMILY;
392 }
393 
394 wxString LV2Effect::GetFamilyName()
395 {
396  return LV2EFFECTS_FAMILY;
397 }
398 
399 bool LV2Effect::IsInteractive()
400 {
401  return mControls.size() != 0;
402 }
403 
404 bool LV2Effect::IsDefault()
405 {
406  return false;
407 }
408 
409 bool LV2Effect::IsLegacy()
410 {
411  return false;
412 }
413 
414 bool LV2Effect::SupportsRealtime()
415 {
416  return GetType() == EffectTypeProcess;
417 }
418 
419 bool LV2Effect::SupportsAutomation()
420 {
421  return true;
422 }
423 
424 // ============================================================================
425 // EffectClientInterface Implementation
426 // ============================================================================
427 
428 bool LV2Effect::SetHost(EffectHostInterface *host)
429 {
430  mHost = host;
431 
432  auto numPorts = lilv_plugin_get_num_ports(mPlug);
433 
434  // Fail if we don't grok the port types
435  for (size_t i = 0; i < numPorts; i++)
436  {
437  const LilvPort *port = lilv_plugin_get_port_by_index(mPlug, i);
438 
439  if (!lilv_port_is_a(mPlug, port, gAudio) &&
440  !lilv_port_is_a(mPlug, port, gControl))
441  {
442  return false;
443  }
444  }
445 
446  {
447  // Allocate buffers for the port indices and the default control values
448  Floats minimumVals{ numPorts };
449  Floats maximumVals{ numPorts };
450  Floats defaultValues{ numPorts };
451 
452  // Retrieve the port ranges for all ports (some values may be NaN)
453  lilv_plugin_get_port_ranges_float(mPlug,
454  minimumVals.get(),
455  maximumVals.get(),
456  defaultValues.get());
457 
458  // Get info about all ports
459  for (size_t i = 0; i < numPorts; i++)
460  {
461  const LilvPort *port = lilv_plugin_get_port_by_index(mPlug, i);
462  int index = lilv_port_get_index(mPlug, port);
463 
464  // Quick check for audio ports
465  if (lilv_port_is_a(mPlug, port, gAudio))
466  {
467  if (lilv_port_is_a(mPlug, port, gInput))
468  {
469  mAudioInputs.push_back(index);
470  }
471  else if (lilv_port_is_a(mPlug, port, gOutput))
472  {
473  mAudioOutputs.push_back(index);
474  }
475  continue;
476  }
477 
478  // Only control ports from this point
479  if (!lilv_port_is_a(mPlug, port, gControl))
480  {
481  continue;
482  }
483 
484  LV2Port ctrl;
485  ctrl.mIndex = index;
486 
487  // Get the port name
488  ctrl.mSymbol = LilvString(lilv_port_get_symbol(mPlug, port));
489  LilvNode *tmpName = lilv_port_get_name(mPlug, port);
490  ctrl.mName = LilvString(tmpName);
491  lilv_node_free(tmpName);
492 
493  // Get any unit descriptor
494  LilvNode *unit = lilv_port_get(mPlug, port, gUnit);
495  if (unit)
496  {
497  ctrl.mUnits = LilvString(lilv_world_get(gWorld, unit, gUnitSymbol, NULL));
498  }
499 
500  // Get the group to which this port belongs or default to the main group
501  ctrl.mGroup = wxEmptyString;
502  LilvNode *group = lilv_port_get(mPlug, port, gGroup);
503  if (group)
504  {
505  ctrl.mGroup = LilvString(lilv_world_get(gWorld, group, gLabel, NULL));
506  if (ctrl.mGroup.IsEmpty())
507  {
508  ctrl.mGroup = LilvString(lilv_world_get(gWorld, group, gName, NULL));
509  }
510 
511  if (ctrl.mGroup.IsEmpty())
512  {
513  ctrl.mGroup = LilvString(group);
514  }
515  }
516 
517  // Add it if not previously done
518  if (mGroups.Index(ctrl.mGroup) == wxNOT_FOUND)
519  {
520  mGroups.Add(ctrl.mGroup);
521  }
522 
523  // Get the scale points
524  LilvScalePoints *points = lilv_port_get_scale_points(mPlug, port);
525  LILV_FOREACH(scale_points, j, points)
526  {
527  const LilvScalePoint *point = lilv_scale_points_get(points, j);
528 
529  ctrl.mScaleValues.push_back(lilv_node_as_float(lilv_scale_point_get_value(point)));
530  ctrl.mScaleLabels.Add(LilvString(lilv_scale_point_get_label(point)));
531  }
532  lilv_scale_points_free(points);
533 
534  // Collect the value and range info
535  ctrl.mHasLo = !std::isnan(minimumVals[i]);
536  ctrl.mHasHi = !std::isnan(maximumVals[i]);
537  ctrl.mMin = ctrl.mHasLo ? minimumVals[i] : 0.0;
538  ctrl.mMax = ctrl.mHasHi ? maximumVals[i] : 1.0;
539  ctrl.mLo = ctrl.mMin;
540  ctrl.mHi = ctrl.mMax;
541  ctrl.mDef = !std::isnan(defaultValues[i]) ?
542  defaultValues[i] :
543  ctrl.mHasLo ?
544  ctrl.mLo :
545  ctrl.mHasHi ?
546  ctrl.mHi :
547  0.0;
548  ctrl.mVal = ctrl.mDef;
549 
550  // Figure out the type of port we have
551  if (lilv_port_is_a(mPlug, port, gInput))
552  {
553  ctrl.mInput = true;
554  if (lilv_port_has_property(mPlug, port, gToggled))
555  {
556  ctrl.mToggle = true;
557  }
558  else if (lilv_port_has_property(mPlug, port, gEnumeration))
559  {
560  ctrl.mEnumeration = true;
561  }
562  else if (lilv_port_has_property(mPlug, port, gInteger))
563  {
564  ctrl.mInteger = true;
565  }
566  else if (lilv_port_has_property(mPlug, port, gSampleRate))
567  {
568  ctrl.mSampleRate = true;
569  }
570 
571  // Trigger properties can be combined with other types, but it
572  // seems mostly to be combined with toggle. So, we turn the
573  // checkbox into a button.
574  if (lilv_port_has_property(mPlug, port, gTrigger))
575  {
576  ctrl.mTrigger = true;
577  }
578 
579  // We'll make the slider logarithmic
580  if (lilv_port_has_property(mPlug, port, gLogarithmic))
581  {
582  ctrl.mLogarithmic = true;
583  }
584 
585  if (lilv_port_has_property(mPlug, port, gEnumeration))
586  {
587  ctrl.mEnumeration = true;
588  }
589 
590  mControlsMap[ctrl.mIndex] = mControls.size();
591  mGroupMap[ctrl.mGroup].push_back(mControls.size());
592  mControls.push_back(ctrl);
593  }
594  else if (lilv_port_is_a(mPlug, port, gOutput))
595  {
596  ctrl.mInput = false;
597  if (lilv_port_has_property(mPlug, port, gLatency))
598  {
599  mLatencyPort = i;
600  }
601  else
602  {
603  mGroupMap[ctrl.mGroup].push_back(mControls.size());
604  mControls.push_back(ctrl);
605  }
606  }
607  else
608  {
609  // Just ignore it for now
610  }
611  }
612  }
613 
614  // mHost will be null during registration
615  if (mHost)
616  {
617  mHost->GetSharedConfig(wxT("Settings"), wxT("UseLatency"), mUseLatency, true);
618  mHost->GetSharedConfig(wxT("Settings"), wxT("UseGUI"), mUseGUI, true);
619 
620  bool haveDefaults;
621  mHost->GetPrivateConfig(mHost->GetFactoryDefaultsGroup(), wxT("Initialized"), haveDefaults, false);
622  if (!haveDefaults)
623  {
624  SaveParameters(mHost->GetFactoryDefaultsGroup());
625  mHost->SetPrivateConfig(mHost->GetFactoryDefaultsGroup(), wxT("Initialized"), true);
626  }
627 
628  LoadParameters(mHost->GetCurrentSettingsGroup());
629  }
630 
631  AddOption(LV2_BUF_SIZE__minBlockLength,
632  sizeof(mBlockSize),
633  LV2_ATOM__Int,
634  &mBlockSize);
635  mBlockSizeOption = AddOption(LV2_BUF_SIZE__maxBlockLength,
636  sizeof(mBlockSize),
637  LV2_ATOM__Int,
638  &mBlockSize);
639  mSampleRateOption = AddOption(LV2_CORE__sampleRate,
640  sizeof(mSampleRate),
641  LV2_ATOM__Double,
642  &mSampleRate);
643  AddOption(NULL, 0, NULL, NULL);
644 
645  mUriMapFeature.callback_data = this;
646  mUriMapFeature.uri_to_id = LV2Effect::uri_to_id;
647 
648  mURIDMapFeature.handle = this;
649  mURIDMapFeature.map = LV2Effect::urid_map;
650 
651  mURIDUnmapFeature.handle = this;
652  mURIDUnmapFeature.unmap = LV2Effect::urid_unmap;
653 
654  mUIResizeFeature.handle = this;
655  mUIResizeFeature.ui_resize = LV2Effect::ui_resize;
656 
657  AddFeature(LV2_UI_PREFIX "makeResident", NULL);
658  AddFeature(LV2_UI__noUserResize, NULL);
659  AddFeature(LV2_BUF_SIZE__boundedBlockLength, NULL);
660  AddFeature(LV2_OPTIONS__options, mOptions.data());
661  AddFeature(LV2_URI_MAP_URI, &mUriMapFeature);
662  AddFeature(LV2_URID__map, &mURIDMapFeature);
663  AddFeature(LV2_URID__unmap, &mURIDUnmapFeature);
664  AddFeature(LV2_UI__resize, &mUIResizeFeature);
665  AddFeature(LV2_DATA_ACCESS_URI, &mExtDataFeature);
666  mInstanceAccessFeature = AddFeature(LV2_INSTANCE_ACCESS_URI, NULL);
667  mParentFeature = AddFeature(LV2_UI__parent, NULL);
668  AddFeature(NULL, NULL);
669 
670  return true;
671 }
672 
673 unsigned LV2Effect::GetAudioInCount()
674 {
675  return mAudioInputs.size();
676 }
677 
678 unsigned LV2Effect::GetAudioOutCount()
679 {
680  return mAudioOutputs.size();
681 }
682 
683 int LV2Effect::GetMidiInCount()
684 {
685  return 0;
686 }
687 
688 int LV2Effect::GetMidiOutCount()
689 {
690  return 0;
691 }
692 
693 void LV2Effect::SetSampleRate(double rate)
694 {
695  mSampleRate = (double) rate;
696 
697  if (mOptionsInterface && mOptionsInterface->set)
698  {
699  LV2_Options_Option options[2]; // 2 for empty terminating option
700  memset(&options, 0, sizeof(options));
701  memcpy(&options, &mOptions[mSampleRateOption], sizeof(mOptions[0]));
702 
703  if (mMaster)
704  {
705  mOptionsInterface->set(lilv_instance_get_handle(mMaster), options);
706  }
707 
708  for (size_t i = 0, cnt = mSlaves.size(); i < cnt; i++)
709  {
710  mOptionsInterface->set(lilv_instance_get_handle(mSlaves[i]), options);
711  }
712  }
713 }
714 
715 size_t LV2Effect::SetBlockSize(size_t maxBlockSize)
716 {
717  mBlockSize = maxBlockSize;
718 
719  if (mOptionsInterface && mOptionsInterface->set)
720  {
721  LV2_Options_Option options[2]; // 2 for empty terminating option
722  memset(&options, 0, sizeof(options));
723  memcpy(&options, &mOptions[mBlockSizeOption], sizeof(mOptions[0]));
724 
725  if (mMaster)
726  {
727  mOptionsInterface->set(lilv_instance_get_handle(mMaster), options);
728  }
729 
730  for (size_t i = 0, cnt = mSlaves.size(); i < cnt; i++)
731  {
732  mOptionsInterface->set(lilv_instance_get_handle(mSlaves[i]), options);
733  }
734  }
735 
736  return mBlockSize;
737 }
738 
739 sampleCount LV2Effect::GetLatency()
740 {
741  if (mUseLatency && mLatencyPort >= 0 && !mLatencyDone)
742  {
743  mLatencyDone = true;
744  return sampleCount( mLatency );
745  }
746 
747  return 0;
748 }
749 
750 size_t LV2Effect::GetTailSize()
751 {
752  return 0;
753 }
754 
755 bool LV2Effect::IsReady()
756 {
757  return mMaster != NULL;
758 }
759 
760 bool LV2Effect::ProcessInitialize(sampleCount WXUNUSED(totalLen), ChannelNames WXUNUSED(chanMap))
761 {
762  mProcess = InitInstance(mSampleRate);
763  if (!mProcess)
764  {
765  return false;
766  }
767 
768  lilv_instance_activate(mProcess);
769 
770  mLatencyDone = false;
771 
772  return true;
773 }
774 
775 bool LV2Effect::ProcessFinalize()
776 {
777  if (mProcess)
778  {
779  lilv_instance_deactivate(mProcess);
780 
781  FreeInstance(mProcess);
782  mProcess = NULL;
783  }
784 
785  return true;
786 }
787 
788 size_t LV2Effect::ProcessBlock(float **inbuf, float **outbuf, size_t size)
789 {
790  for (size_t p = 0, cnt = mAudioInputs.size(); p < cnt; p++)
791  {
792  lilv_instance_connect_port(mProcess, mAudioInputs[p], inbuf[p]);
793  }
794 
795  for (size_t p = 0, cnt = mAudioOutputs.size(); p < cnt; p++)
796  {
797  lilv_instance_connect_port(mProcess, mAudioOutputs[p], outbuf[p]);
798  }
799 
800  lilv_instance_run(mProcess, size);
801 
802  return size;
803 }
804 
805 bool LV2Effect::RealtimeInitialize()
806 {
807  mMasterIn.reinit( mAudioInputs.size(), mBlockSize );
808  for (size_t p = 0, cnt = mAudioInputs.size(); p < cnt; p++)
809  lilv_instance_connect_port(mMaster, mAudioInputs[p], mMasterIn[p].get());
810 
811  mMasterOut.reinit( mAudioOutputs.size(), mBlockSize );
812  for (size_t p = 0, cnt = mAudioOutputs.size(); p < cnt; p++)
813  lilv_instance_connect_port(mMaster, mAudioOutputs[p], mMasterOut[p].get());
814 
815  lilv_instance_activate(mMaster);
816 
817  return true;
818 }
819 
820 bool LV2Effect::RealtimeFinalize()
821 {
822  for (size_t i = 0, cnt = mSlaves.size(); i < cnt; i++)
823  {
824  lilv_instance_deactivate(mSlaves[i]);
825 
826  FreeInstance(mSlaves[i]);
827  }
828  mSlaves.clear();
829 
830  lilv_instance_deactivate(mMaster);
831 
832  mMasterIn.reset();
833 
834  mMasterOut.reset();
835 
836  return true;
837 }
838 
839 bool LV2Effect::RealtimeSuspend()
840 {
841  return true;
842 }
843 
844 bool LV2Effect::RealtimeResume()
845 {
846  return true;
847 }
848 
849 bool LV2Effect::RealtimeProcessStart()
850 {
851  for (size_t p = 0, cnt = mAudioInputs.size(); p < cnt; p++)
852  memset(mMasterIn[p].get(), 0, mBlockSize * sizeof(float));
853 
854  mNumSamples = 0;
855 
856  return true;
857 }
858 
859 size_t LV2Effect::RealtimeProcess(int group,
860  float **inbuf,
861  float **outbuf,
862  size_t numSamples)
863 {
864  wxASSERT(group >= 0 && group < (int) mSlaves.size());
865  wxASSERT(numSamples <= mBlockSize);
866 
867  if (group < 0 || group >= (int) mSlaves.size())
868  {
869  return 0;
870  }
871 
872  for (size_t p = 0, cnt = mAudioInputs.size(); p < cnt; p++)
873  {
874  for (decltype(numSamples) s = 0; s < numSamples; s++)
875  {
876  mMasterIn[p][s] += inbuf[p][s];
877  }
878  }
879  mNumSamples = wxMax(numSamples, mNumSamples);
880 
881  LilvInstance *slave = mSlaves[group];
882 
883  for (size_t p = 0, cnt = mAudioInputs.size(); p < cnt; p++)
884  {
885  lilv_instance_connect_port(slave, mAudioInputs[p], inbuf[p]);
886  }
887 
888  for (size_t p = 0, cnt = mAudioOutputs.size(); p < cnt; p++)
889  {
890  lilv_instance_connect_port(slave, mAudioOutputs[p], outbuf[p]);
891  }
892 
893  lilv_instance_run(slave, numSamples);
894 
895  return numSamples;
896 }
897 
898 bool LV2Effect::RealtimeAddProcessor(unsigned WXUNUSED(numChannels), float sampleRate)
899 {
900  LilvInstance *slave = InitInstance(sampleRate);
901  if (!slave)
902  {
903  return false;
904  }
905 
906  lilv_instance_activate(slave);
907 
908  mSlaves.push_back(slave);
909 
910  return true;
911 }
912 
913 bool LV2Effect::RealtimeProcessEnd()
914 {
915  lilv_instance_run(mMaster, mNumSamples);
916 
917  return true;
918 }
919 
920 bool LV2Effect::ShowInterface(wxWindow *parent, bool forceModal)
921 {
922  if (mDialog)
923  {
924  if ( mDialog->Close(true) )
925  mDialog = nullptr;
926  return false;
927  }
928 
929  // mDialog is null
930  auto cleanup = valueRestorer( mDialog );
931 
932  mDialog = mHost->CreateUI(parent, this);
933  if (!mDialog)
934  {
935  return false;
936  }
937 
938  // Try to give the window a sensible default/minimum size
939  mDialog->Layout();
940  mDialog->Fit();
941  mDialog->SetMinSize(mDialog->GetSize());
942 
943  if ((SupportsRealtime() || GetType() == EffectTypeAnalyze) && !forceModal)
944  {
945  mDialog->Show();
946  cleanup.release();
947 
948  return false;
949  }
950 
951  bool res = mDialog->ShowModal() != 0;
952 
953  return res;
954 }
955 
956 bool LV2Effect::GetAutomationParameters(EffectAutomationParameters & parms)
957 {
958  for (size_t p = 0, cnt = mControls.size(); p < cnt; p++)
959  {
960  if (mControls[p].mInput)
961  {
962  if (!parms.Write(mControls[p].mName, mControls[p].mVal))
963  {
964  return false;
965  }
966  }
967  }
968 
969  return true;
970 }
971 
972 bool LV2Effect::SetAutomationParameters(EffectAutomationParameters & parms)
973 {
974  // First pass validates values
975  for (size_t p = 0, cnt = mControls.size(); p < cnt; p++)
976  {
977  LV2Port & ctrl = mControls[p];
978 
979  if (ctrl.mInput)
980  {
981  double d = 0.0;
982  if (!parms.Read(ctrl.mName, &d))
983  {
984  return false;
985  }
986 
987  // Use unscaled range here
988  if (d < ctrl.mMin || d > ctrl.mMax)
989  {
990  return false;
991  }
992  }
993  }
994 
995  // Second pass actually sets the values
996  for (size_t p = 0, cnt = mControls.size(); p < cnt; p++)
997  {
998  LV2Port & ctrl = mControls[p];
999 
1000  if (ctrl.mInput)
1001  {
1002  double d = 0.0;
1003  if (!parms.Read(ctrl.mName, &d))
1004  {
1005  return false;
1006  }
1007 
1008  ctrl.mVal = d;
1009  ctrl.mTmp = ctrl.mVal * (ctrl.mSampleRate ? mSampleRate : 1.0);
1010  }
1011  }
1012 
1013  return true;
1014 }
1015 
1016 // ============================================================================
1017 // EffectUIClientInterface Implementation
1018 // ============================================================================
1019 
1020 void LV2Effect::SetHostUI(EffectUIHostInterface *host)
1021 {
1022  mUIHost = host;
1023 }
1024 
1025 bool LV2Effect::PopulateUI(wxWindow *parent)
1026 {
1027  mParent = parent;
1028 
1029  mParent->PushEventHandler(this);
1030 
1031  mSuilHost = NULL;
1032  mSuilInstance = NULL;
1033 
1034  mMaster = InitInstance(mSampleRate);
1035  if (mMaster == NULL)
1036  {
1037  AudacityMessageBox(_("Couldn't instantiate effect"));
1038  return false;
1039  }
1040 
1041  // Determine if the GUI editor is supposed to be used or not
1042  mHost->GetSharedConfig(wxT("Settings"),
1043  wxT("UseGUI"),
1044  mUseGUI,
1045  true);
1046 
1047  // Until I figure out where to put the "Duration" control in the
1048  // graphical editor, force usage of plain editor.
1049  if (GetType() == EffectTypeGenerate)
1050  {
1051  mUseGUI = false;
1052  }
1053 
1054  if (mUseGUI)
1055  {
1056  mUseGUI = BuildFancy();
1057  }
1058 
1059  if (!mUseGUI)
1060  {
1061  return BuildPlain();
1062  }
1063 
1064  return true;
1065 }
1066 
1067 bool LV2Effect::IsGraphicalUI()
1068 {
1069  return mUseGUI;
1070 }
1071 
1072 bool LV2Effect::ValidateUI()
1073 {
1074  if (!mParent->Validate() || !mParent->TransferDataFromWindow())
1075  {
1076  return false;
1077  }
1078 
1079  if (GetType() == EffectTypeGenerate)
1080  {
1081  mHost->SetDuration(mDuration->GetValue());
1082  }
1083 
1084  return true;
1085 }
1086 
1087 bool LV2Effect::HideUI()
1088 {
1089  return true;
1090 }
1091 
1092 bool LV2Effect::CloseUI()
1093 {
1094 #ifdef __WXMAC__
1095 #ifdef __WX_EVTLOOP_BUSY_WAITING__
1096  wxEventLoop::SetBusyWaiting(false);
1097 #endif
1098 #endif
1099 
1100  mParent->RemoveEventHandler(this);
1101 
1102  mSliders.reset();
1103  mFields.reset();
1104 
1105  if (mSuilInstance)
1106  {
1107  mIdleFeature = NULL;
1108  suil_instance_free(mSuilInstance);
1109  mSuilInstance = NULL;
1110  }
1111 
1112  if (mSuilHost)
1113  {
1114  suil_host_free(mSuilHost);
1115  mSuilHost = NULL;
1116  }
1117 
1118  if (mMaster)
1119  {
1120  FreeInstance(mMaster);
1121  mMaster = NULL;
1122  }
1123 
1124  mUIHost = NULL;
1125  mParent = NULL;
1126  mDialog = NULL;
1127 
1128  return true;
1129 }
1130 
1131 bool LV2Effect::LoadUserPreset(const wxString & name)
1132 {
1133  if (!LoadParameters(name))
1134  {
1135  return false;
1136  }
1137 
1138  return TransferDataToWindow();
1139 }
1140 
1141 bool LV2Effect::SaveUserPreset(const wxString & name)
1142 {
1143  return SaveParameters(name);
1144 }
1145 
1146 wxArrayString LV2Effect::GetFactoryPresets()
1147 {
1148  if (mFactoryPresetsLoaded)
1149  {
1150  return mFactoryPresetNames;
1151  }
1152 
1153  LilvNodes* presets = lilv_plugin_get_related(mPlug, gPreset);
1154  if (presets)
1155  {
1156  LILV_FOREACH(nodes, i, presets)
1157  {
1158  const LilvNode *preset = lilv_nodes_get(presets, i);
1159 
1160  mFactoryPresetUris.Add(LilvString(preset));
1161 
1162  lilv_world_load_resource(gWorld, preset);
1163 
1164  LilvNodes *labels = lilv_world_find_nodes(gWorld, preset, gLabel, NULL);
1165  if (labels)
1166  {
1167  const LilvNode *label = lilv_nodes_get_first(labels);
1168 
1169  mFactoryPresetNames.Add(LilvString(label));
1170 
1171  lilv_nodes_free(labels);
1172  }
1173  else
1174  {
1175  mFactoryPresetNames.Add(LilvString(preset).AfterLast(wxT('#')));
1176  }
1177  }
1178 
1179  lilv_nodes_free(presets);
1180  }
1181 
1182  mFactoryPresetsLoaded = true;
1183 
1184  return mFactoryPresetNames;
1185 }
1186 
1187 bool LV2Effect::LoadFactoryPreset(int id)
1188 {
1189  if (id < 0 || id >= (int) mFactoryPresetUris.GetCount())
1190  {
1191  return false;
1192  }
1193 
1194  LilvNode *preset = lilv_new_uri(gWorld, mFactoryPresetUris[id].ToUTF8());
1195  if (!preset)
1196  {
1197  return false;
1198  }
1199 
1200  LilvState *state = lilv_state_new_from_world(gWorld, &mURIDMapFeature, preset);
1201  if (state)
1202  {
1203  lilv_state_restore(state, mMaster, set_value_func, this, 0, NULL);
1204 
1205  lilv_state_free(state);
1206 
1207  TransferDataToWindow();
1208  }
1209 
1210  lilv_node_free(preset);
1211 
1212  return state != NULL;
1213 }
1214 
1215 bool LV2Effect::LoadFactoryDefaults()
1216 {
1217  if (!LoadParameters(mHost->GetFactoryDefaultsGroup()))
1218  {
1219  return false;
1220  }
1221 
1222  return TransferDataToWindow();
1223 }
1224 
1225 bool LV2Effect::CanExportPresets()
1226 {
1227  return false;
1228 }
1229 
1230 void LV2Effect::ExportPresets()
1231 {
1232 }
1233 
1234 void LV2Effect::ImportPresets()
1235 {
1236 }
1237 
1238 bool LV2Effect::HasOptions()
1239 {
1240  return true;
1241 }
1242 
1243 void LV2Effect::ShowOptions()
1244 {
1245  LV2EffectSettingsDialog dlg(mParent, this);
1246  if (dlg.ShowModal() == wxID_OK)
1247  {
1248  mHost->GetSharedConfig(wxT("Settings"), wxT("UseLatency"), mUseLatency, true);
1249  }
1250 }
1251 
1252 // ============================================================================
1253 // LV2Effect Implementation
1254 // ============================================================================
1255 
1256 bool LV2Effect::LoadParameters(const wxString & group)
1257 {
1258  wxString parms;
1259  if (!mHost->GetPrivateConfig(group, wxT("Parameters"), parms, wxEmptyString))
1260  {
1261  return false;
1262  }
1263 
1264  EffectAutomationParameters eap;
1265  if (!eap.SetParameters(parms))
1266  {
1267  return false;
1268  }
1269 
1270  return SetAutomationParameters(eap);
1271 }
1272 
1273 bool LV2Effect::SaveParameters(const wxString & group)
1274 {
1275  EffectAutomationParameters eap;
1276  if (!GetAutomationParameters(eap))
1277  {
1278  return false;
1279  }
1280 
1281  wxString parms;
1282  if (!eap.GetParameters(parms))
1283  {
1284  return false;
1285  }
1286 
1287  return mHost->SetPrivateConfig(group, wxT("Parameters"), parms);
1288 }
1289 
1290 size_t LV2Effect::AddOption(const char *key, uint32_t size, const char *type, void *value)
1291 {
1292  int ndx = mOptions.size();
1293 
1294  mOptions.resize(1 + mOptions.size());
1295  memset(&mOptions[ndx], 0, sizeof(mOptions[ndx]));
1296 
1297  if (key != NULL)
1298  {
1299  mOptions[ndx].context = LV2_OPTIONS_INSTANCE;
1300  mOptions[ndx].subject = 0;
1301  mOptions[ndx].key = URID_Map(key);
1302  mOptions[ndx].size = size;
1303  mOptions[ndx].type = URID_Map(type);
1304  mOptions[ndx].value = value;
1305  }
1306 
1307  return ndx;
1308 }
1309 
1310 LV2_Feature *LV2Effect::AddFeature(const char *uri, void *data)
1311 {
1312  size_t ndx = mFeatures.size();
1313  mFeatures.resize(1 + mFeatures.size());
1314 
1315  if (uri != NULL)
1316  {
1317  mFeatures[ndx].reset( safenew LV2_Feature );
1318  mFeatures[ndx]->URI = uri;
1319  mFeatures[ndx]->data = data;
1320  }
1321 
1322  return mFeatures[ndx].get();
1323 }
1324 
1325 LilvInstance *LV2Effect::InitInstance(float sampleRate)
1326 {
1327  LilvInstance *handle = lilv_plugin_instantiate(
1328  mPlug, sampleRate,
1329  reinterpret_cast<const LV2_Feature *const *>(mFeatures.data()));
1330  if (!handle)
1331  {
1332  return NULL;
1333  }
1334 
1335  mOptionsInterface = (LV2_Options_Interface *)
1336  lilv_instance_get_extension_data(handle, LV2_OPTIONS__interface);
1337 
1338  SetBlockSize(mBlockSize);
1339  SetSampleRate(sampleRate);
1340 
1341  for (size_t p = 0, cnt = mControls.size(); p < cnt; p++)
1342  {
1343  lilv_instance_connect_port(handle,
1344  mControls[p].mIndex,
1345  &mControls[p].mVal);
1346  }
1347 
1348  if (mLatencyPort >= 0)
1349  {
1350  lilv_instance_connect_port(handle, mLatencyPort, &mLatency);
1351  }
1352 
1353  return handle;
1354 }
1355 
1356 void LV2Effect::FreeInstance(LilvInstance *handle)
1357 {
1358  lilv_instance_free(handle);
1359 }
1360 
1361 bool LV2Effect::BuildFancy()
1362 {
1363  // Set the native UI type
1364  const char *nativeType =
1365 #if defined(__WXGTK__)
1366  LV2_UI__GtkUI;
1367 #elif defined(__WXMSW__)
1368  LV2_UI__WindowsUI;
1369 #elif defined(__WXMAC__)
1370  LV2_UI__CocoaUI;
1371 #endif
1372 
1373  // Determine if the plugin has a supported UI
1374  const LilvUI *ui = NULL;
1375  const LilvNode *uiType = NULL;
1376  LilvUIs *uis = lilv_plugin_get_uis(mPlug);
1377  if (uis)
1378  {
1379  LilvNode *containerType = lilv_new_uri(gWorld, nativeType);
1380  if (containerType)
1381  {
1382  LILV_FOREACH(uis, iter, uis)
1383  {
1384  ui = lilv_uis_get(uis, iter);
1385  if (lilv_ui_is_supported(ui, suil_ui_supported, containerType, &uiType))
1386  {
1387  break;
1388  }
1389  ui = NULL;
1390  }
1391 
1392  lilv_node_free(containerType);
1393  }
1394  }
1395 
1396  // No usable UI found
1397  if (ui == NULL)
1398  {
1399  lilv_uis_free(uis);
1400  return false;
1401  }
1402 
1403  // Use a panel to host the plugins GUI
1404  // container is owned by mParent, but we may destroy it if there are
1405  // any errors before completing the build of UI.
1406  auto container = Destroy_ptr<wxPanelWrapper>{
1407  safenew wxPanelWrapper{ mParent, wxID_ANY}
1408  };
1409  if (!container)
1410  {
1411  lilv_uis_free(uis);
1412  return false;
1413  }
1414 
1415  {
1416  auto vs = std::make_unique<wxBoxSizer>(wxVERTICAL);
1417  wxSizerItem *si = NULL;
1418  if (vs)
1419  {
1420  auto hs = std::make_unique<wxBoxSizer>(wxHORIZONTAL);
1421  if (hs)
1422  {
1423  si = hs->Add(container.get(), 1, wxCENTER | wxEXPAND);
1424  vs->Add(hs.release(), 0, wxCENTER);
1425  }
1426  }
1427 
1428  if (!si)
1429  {
1430  lilv_uis_free(uis);
1431  return false;
1432  }
1433 
1434 #if defined(__WXGTK__)
1435  // Make sure the parent has a window
1436  if (!gtk_widget_get_window(GTK_WIDGET(container->m_wxwindow)))
1437  {
1438  gtk_widget_realize(GTK_WIDGET(container->m_wxwindow));
1439  }
1440 
1441  mParentFeature->data = GTK_WIDGET(container->GetHandle());
1442 #elif defined(__WXMSW__)
1443  mParentFeature->data = container->GetHandle();
1444 #elif defined(__WXMAC__)
1445  mParentFeature->data = container->GetHandle();
1446 #endif
1447 
1448  mInstanceAccessFeature->data = lilv_instance_get_handle(mMaster);
1449  mExtDataFeature.data_access = lilv_instance_get_descriptor(mMaster)->extension_data;
1450 
1451  // Create the suil host
1452  mSuilHost = suil_host_new(LV2Effect::suil_write_func, NULL, NULL, NULL);
1453  if (!mSuilHost)
1454  {
1455  lilv_uis_free(uis);
1456  return false;
1457  }
1458 
1459  mSuilInstance = suil_instance_new(mSuilHost,
1460  this,
1461  nativeType,
1462  lilv_node_as_uri(lilv_plugin_get_uri(mPlug)),
1463  lilv_node_as_uri(lilv_ui_get_uri(ui)),
1464  lilv_node_as_uri(uiType),
1465  lilv_uri_to_path(lilv_node_as_uri(lilv_ui_get_bundle_uri(ui))),
1466  lilv_uri_to_path(lilv_node_as_uri(lilv_ui_get_binary_uri(ui))),
1467  reinterpret_cast<const LV2_Feature *const *>(mFeatures.data()));
1468 
1469  lilv_uis_free(uis);
1470 
1471  // Bail if the instance (no compatible UI) couldn't be created
1472  if (!mSuilInstance)
1473  {
1474  suil_host_free(mSuilHost);
1475  mSuilHost = NULL;
1476 
1477  return false;
1478  }
1479 
1480 #if defined(__WXGTK__)
1481  GtkWidget* widget = GTK_WIDGET(suil_instance_get_widget(mSuilInstance));
1482  gtk_widget_show_all(widget);
1483 
1484  GtkRequisition sz;
1485  gtk_widget_size_request(widget, &sz);
1486  gtk_widget_set_size_request(widget, 1, 1);
1487  gtk_widget_set_size_request(widget, sz.width, sz.height);
1488 
1489  wxPizza *pizza = WX_PIZZA(container->m_wxwindow);
1490  pizza->put(widget,
1491  0, //gtk_pizza_get_xoffset(pizza),
1492  0, //gtk_pizza_get_yoffset(pizza),
1493  sz.width,
1494  sz.height);
1495  gtk_widget_show_all(GTK_WIDGET(pizza));
1496  si->SetMinSize(wxSize(sz.width, sz.height));
1497 #elif defined(__WXMSW__)
1498  HWND widget = (HWND)suil_instance_get_widget(mSuilInstance);
1499  RECT rect;
1500  GetWindowRect(widget, &rect);
1501  si->SetMinSize(wxSize(rect.right - rect.left, rect.bottom - rect.top));
1502 #elif defined(__WXMAC__)
1503  NSView *view = (NSView *) suil_instance_get_widget(mSuilInstance);
1504  NSSize sz = [view frame].size;
1505  si->SetMinSize(sz.width, sz.height);
1506 #endif
1507 
1508  mParent->SetSizerAndFit(vs.release());
1509  // mParent will guarantee release of the container now.
1510  container.release();
1511  }
1512 
1513  mIdleFeature = (const LV2UI_Idle_Interface *)
1514  suil_instance_extension_data(mSuilInstance, LV2_UI__idleInterface);
1515 
1516  TransferDataToWindow();
1517 
1518 #ifdef __WXMAC__
1519 #ifdef __WX_EVTLOOP_BUSY_WAITING__
1520  wxEventLoop::SetBusyWaiting(true);
1521 #endif
1522 #endif
1523 
1524  return true;
1525 }
1526 
1527 bool LV2Effect::BuildPlain()
1528 {
1529  int numCols = 5;
1530 
1531  // Allocate memory for the user parameter controls
1532  auto ctrlcnt = mControls.size();
1533  mSliders.reinit(ctrlcnt);
1534  mFields.reinit(ctrlcnt);
1535 
1536  wxSizer *innerSizer;
1537 
1538  wxASSERT(mParent); // To justify safenew
1539  wxScrolledWindow *const w = safenew wxScrolledWindow(mParent,
1540  wxID_ANY,
1541  wxDefaultPosition,
1542  wxDefaultSize,
1543  wxVSCROLL | wxTAB_TRAVERSAL);
1544 
1545  {
1546  auto outerSizer = std::make_unique<wxBoxSizer>(wxVERTICAL);
1547  w->SetScrollRate(0, 20);
1548 
1549  // This fools NVDA into not saying "Panel" when the dialog gets focus
1550  w->SetName(wxT("\a"));
1551  w->SetLabel(wxT("\a"));
1552 
1553  outerSizer->Add(w, 1, wxEXPAND);
1554 
1555  {
1556  auto uInnerSizer = std::make_unique<wxBoxSizer>(wxVERTICAL);
1557  innerSizer = uInnerSizer.get();
1558 
1559  if (GetType() == EffectTypeGenerate)
1560  {
1561  // Add the length control
1562  auto groupSizer = std::make_unique<wxStaticBoxSizer>(wxVERTICAL, w, _("Generator"));
1563 
1564  auto sizer = std::make_unique<wxBoxSizer>(wxHORIZONTAL);
1565 
1566  wxWindow *item = safenew wxStaticText(w, 0, _("&Duration:"));
1567  sizer->Add(item, 0, wxALIGN_CENTER | wxALL, 5);
1568  mDuration = safenew
1571  mHost->GetDurationFormat(),
1572  mHost->GetDuration(),
1573  mSampleRate,
1575  .AutoPos(true));
1576  mDuration->SetName(_("Duration"));
1577  sizer->Add(mDuration, 0, wxALIGN_CENTER | wxALL, 5);
1578 
1579  groupSizer->Add(sizer.release(), 0, wxALIGN_CENTER | wxALL, 5);
1580  innerSizer->Add(groupSizer.release(), 0, wxEXPAND | wxALL, 5);
1581  }
1582 
1583  mGroups.Sort();
1584 
1585  for (size_t i = 0, cnt = mGroups.GetCount(); i < cnt; i++)
1586  {
1587  wxString label = mGroups[i];
1588  if (label.IsEmpty())
1589  {
1590  label = _("Effect Settings");
1591  }
1592  auto groupSizer = std::make_unique<wxStaticBoxSizer>(wxVERTICAL, w, label);
1593 
1594  auto gridSizer = std::make_unique<wxFlexGridSizer>(numCols, 5, 5);
1595  gridSizer->AddGrowableCol(3);
1596 
1597  const auto & params = mGroupMap[mGroups[i]];
1598  for (size_t pi = 0, cnt = params.size(); pi < cnt; pi++)
1599  {
1600  int p = params[pi];
1601  LV2Port & ctrl = mControls[p];
1602  wxString labelText = ctrl.mName;
1603  if (!ctrl.mUnits.IsEmpty())
1604  {
1605  labelText += wxT(" (") + ctrl.mUnits + wxT(")");
1606  }
1607 
1608  if (ctrl.mTrigger)
1609  {
1610  gridSizer->Add(1, 1, 0);
1611 
1612  wxASSERT(w); // To justify safenew
1613  wxButton *b = safenew wxButton(w, ID_Triggers + p, labelText);
1614  gridSizer->Add(b, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_LEFT);
1615 
1616  gridSizer->Add(1, 1, 0);
1617  gridSizer->Add(1, 1, 0);
1618  gridSizer->Add(1, 1, 0);
1619  continue;
1620  }
1621 
1622  wxWindow *item = safenew wxStaticText(w, wxID_ANY, labelText + wxT(":"),
1623  wxDefaultPosition, wxDefaultSize,
1624  wxALIGN_RIGHT);
1625  gridSizer->Add(item, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT);
1626 
1627  if (ctrl.mToggle)
1628  {
1629  wxCheckBox *c = safenew wxCheckBox(w, ID_Toggles + p, wxT(""));
1630  c->SetName(labelText);
1631  c->SetValue(ctrl.mVal > 0);
1632  gridSizer->Add(c, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_LEFT);
1633 
1634  gridSizer->Add(1, 1, 0);
1635  gridSizer->Add(1, 1, 0);
1636  gridSizer->Add(1, 1, 0);
1637  }
1638  else if (ctrl.mEnumeration) // Check before integer
1639  {
1640  int s;
1641  for (s = (int)ctrl.mScaleValues.size() - 1; s >= 0; s--)
1642  {
1643  if (ctrl.mVal >= ctrl.mScaleValues[s])
1644  {
1645  break;
1646  }
1647  }
1648 
1649  if (s < 0)
1650  {
1651  s = 0;
1652  }
1653 
1654  wxChoice *c = safenew wxChoice(w, ID_Choices + p);
1655  c->SetName(labelText);
1656  c->Append(ctrl.mScaleLabels);
1657  c->SetSelection(s);
1658  gridSizer->Add(c, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_LEFT);
1659 
1660  gridSizer->Add(1, 1, 0);
1661  gridSizer->Add(1, 1, 0);
1662  gridSizer->Add(1, 1, 0);
1663  }
1664  else if (!ctrl.mInput)
1665  {
1666  gridSizer->Add(1, 1, 0);
1667  gridSizer->Add(1, 1, 0);
1668  LV2EffectMeter *m = safenew LV2EffectMeter(w, ctrl);
1669  gridSizer->Add(m, 0, wxALIGN_CENTER_VERTICAL | wxEXPAND);
1670  gridSizer->Add(1, 1, 0);
1671  }
1672  else
1673  {
1674  mFields[p] = safenew wxTextCtrl(w, ID_Texts + p, wxT(""));
1675  mFields[p]->SetName(labelText);
1676  gridSizer->Add(mFields[p], 0, wxALIGN_CENTER_VERTICAL | wxALIGN_LEFT);
1677 
1678  float rate = ctrl.mSampleRate ? mSampleRate : 1.0;
1679 
1680  ctrl.mLo = ctrl.mMin * rate;
1681  ctrl.mHi = ctrl.mMax * rate;
1682  ctrl.mTmp = ctrl.mVal * rate;
1683 
1684  if (ctrl.mInteger)
1685  {
1686  IntegerValidator<float> vld(&ctrl.mTmp);
1687  vld.SetRange(ctrl.mLo, ctrl.mHi);
1688  mFields[p]->SetValidator(vld);
1689  }
1690  else
1691  {
1692  FloatingPointValidator<float> vld(6, &ctrl.mTmp);
1693  vld.SetRange(ctrl.mLo, ctrl.mHi);
1694 
1695  // Set number of decimal places
1696  float range = ctrl.mHi - ctrl.mLo;
1697  auto style = range < 10 ? NumValidatorStyle::THREE_TRAILING_ZEROES :
1698  range < 100 ? NumValidatorStyle::TWO_TRAILING_ZEROES :
1699  NumValidatorStyle::ONE_TRAILING_ZERO;
1700  vld.SetStyle(style);
1701 
1702  mFields[p]->SetValidator(vld);
1703  }
1704 
1705  if (ctrl.mHasLo)
1706  {
1707  wxString str;
1708  if (ctrl.mInteger || ctrl.mSampleRate)
1709  {
1710  str.Printf(wxT("%d"), lrintf(ctrl.mLo));
1711  }
1712  else
1713  {
1714  str = Internat::ToDisplayString(ctrl.mLo);
1715  }
1716  item = safenew wxStaticText(w, wxID_ANY, str);
1717  gridSizer->Add(item, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT);
1718  }
1719  else
1720  {
1721  gridSizer->Add(1, 1, 0);
1722  }
1723 
1724  mSliders[p] = safenew wxSlider(w, ID_Sliders + p,
1725  0, 0, 1000,
1726  wxDefaultPosition,
1727  wxSize(150, -1));
1728  mSliders[p]->SetName(labelText);
1729  gridSizer->Add(mSliders[p], 0, wxALIGN_CENTER_VERTICAL | wxEXPAND);
1730 
1731  if (ctrl.mHasHi)
1732  {
1733  wxString str;
1734  if (ctrl.mInteger || ctrl.mSampleRate)
1735  {
1736  str.Printf(wxT("%d"), lrintf(ctrl.mHi));
1737  }
1738  else
1739  {
1740  str = Internat::ToDisplayString(ctrl.mHi);
1741  }
1742  item = safenew wxStaticText(w, wxID_ANY, str);
1743  gridSizer->Add(item, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_LEFT);
1744  }
1745  else
1746  {
1747  gridSizer->Add(1, 1, 0);
1748  }
1749  }
1750  }
1751 
1752  groupSizer->Add(gridSizer.release(), 1, wxEXPAND | wxALL, 5);
1753  innerSizer->Add(groupSizer.release(), 0, wxEXPAND | wxALL, 5);
1754  }
1755 
1756  innerSizer->Layout();
1757 
1758  // Calculate the maximum width of all columns (bypass Generator sizer)
1759  std::vector<int> widths(numCols);
1760 
1761  size_t cnt = innerSizer->GetChildren().GetCount();
1762  for (size_t i = (GetType() == EffectTypeGenerate); i < cnt; i++)
1763  {
1764  wxSizer *groupSizer = innerSizer->GetItem(i)->GetSizer();
1765  wxFlexGridSizer *gridSizer = (wxFlexGridSizer *)groupSizer->GetItem((size_t)0)->GetSizer();
1766 
1767  size_t items = gridSizer->GetChildren().GetCount();
1768  int cols = gridSizer->GetCols();
1769 
1770  for (size_t j = 0; j < items; j++)
1771  {
1772  wxSizerItem *item = gridSizer->GetItem(j);
1773  widths[j % cols] = wxMax(widths[j % cols], item->GetSize().GetWidth());
1774  }
1775  }
1776 
1777  // Set each column in all of the groups to the same width.
1778  for (size_t i = (GetType() == EffectTypeGenerate); i < cnt; i++)
1779  {
1780  wxSizer *groupSizer = innerSizer->GetItem(i)->GetSizer();
1781  wxFlexGridSizer *gridSizer = (wxFlexGridSizer *)groupSizer->GetItem((size_t)0)->GetSizer();
1782 
1783  size_t items = gridSizer->GetChildren().GetCount();
1784  int cols = gridSizer->GetCols();
1785 
1786  for (size_t j = 0; j < items; j++)
1787  {
1788  wxSizerItem *item = gridSizer->GetItem(j);
1789 
1790  int flags = item->GetFlag();
1791  if (flags & wxEXPAND)
1792  {
1793  continue;
1794  }
1795 
1796  if (flags & wxALIGN_RIGHT)
1797  {
1798  flags = (flags & ~wxALL) | wxLEFT;
1799  }
1800  else
1801  {
1802  flags = (flags & ~wxALL) | wxRIGHT;
1803  }
1804  item->SetFlag(flags);
1805 
1806  item->SetBorder(widths[j % cols] - item->GetMinSize().GetWidth());
1807  }
1808  }
1809 
1810  w->SetSizer(uInnerSizer.release());
1811  }
1812 
1813  mParent->SetSizer(outerSizer.release());
1814  }
1815 
1816  // Try to give the window a sensible default/minimum size
1817  wxSize sz1 = innerSizer->GetMinSize();
1818  wxSize sz2 = mParent->GetMinSize();
1819  w->SetSizeHints(wxSize(-1, wxMin(sz1.y, sz2.y)));
1820 
1821  // And let the parent reduce to the NEW minimum if possible
1822  mParent->SetSizeHints(w->GetMinSize());
1823 
1824  TransferDataToWindow();
1825 
1826  return true;
1827 }
1828 
1829 bool LV2Effect::TransferDataToWindow()
1830 {
1831  if (mUseGUI)
1832  {
1833  if (mSuilInstance)
1834  {
1835  for (size_t p = 0, cnt = mControls.size(); p < cnt; p++)
1836  {
1837  if (mControls[p].mInput)
1838  {
1839  suil_instance_port_event(mSuilInstance,
1840  mControls[p].mIndex,
1841  sizeof(float),
1842  0,
1843  &mControls[p].mVal);
1844  }
1845  }
1846  }
1847 
1848  return true;
1849  }
1850 
1851  for (size_t i = 0, cnt = mGroups.GetCount(); i < cnt; i++)
1852  {
1853  const auto & params = mGroupMap[mGroups[i]];
1854  for (size_t pi = 0, cnt = params.size(); pi < cnt; pi++)
1855  {
1856  int p = params[pi];
1857  LV2Port & ctrl = mControls[p];
1858 
1859  if (ctrl.mTrigger)
1860  {
1861  continue;
1862  }
1863 
1864  if (ctrl.mToggle)
1865  {
1866  wxCheckBox *c = wxDynamicCast(mParent->FindWindow(ID_Toggles + p), wxCheckBox);
1867  if (c)
1868  c->SetValue(ctrl.mVal > 0);
1869  }
1870  else if (ctrl.mEnumeration) // Check before integer
1871  {
1872  int s;
1873  for (s = (int) ctrl.mScaleValues.size() - 1; s >= 0; s--)
1874  {
1875  if (ctrl.mVal >= ctrl.mScaleValues[s])
1876  {
1877  break;
1878  }
1879  }
1880 
1881  if (s < 0)
1882  {
1883  s = 0;
1884  }
1885 
1886  wxChoice *c = wxDynamicCast(mParent->FindWindow(ID_Choices + p), wxChoice);
1887  if (c)
1888  c->SetSelection(s);
1889  }
1890  else if (ctrl.mInput)
1891  {
1892  ctrl.mTmp = ctrl.mVal * (ctrl.mSampleRate ? mSampleRate : 1.0);
1893  if (mSliders && mSliders[p])
1894  SetSlider(mSliders[p], ctrl);
1895  }
1896  }
1897  }
1898 
1899  if (mParent && !mParent->TransferDataToWindow())
1900  {
1901  return false;
1902  }
1903 
1904  return true;
1905 }
1906 
1907 bool LV2Effect::TransferDataFromWindow()
1908 {
1909  if (!mParent->Validate() || !mParent->TransferDataFromWindow())
1910  {
1911  return false;
1912  }
1913 
1914  return true;
1915 }
1916 
1917 void LV2Effect::SetSlider(wxSlider *slider, const LV2Port & ctrl)
1918 {
1919  float lo = ctrl.mLo;
1920  float hi = ctrl.mHi;
1921  float val = ctrl.mTmp;
1922 
1923  if (ctrl.mLogarithmic)
1924  {
1925  lo = logf(lo);
1926  hi = logf(hi);
1927  val = logf(val);
1928  }
1929 
1930  slider->SetValue(lrintf((val - lo) / (hi - lo) * 1000.0));
1931 }
1932 
1933 void LV2Effect::OnTrigger(wxCommandEvent & evt)
1934 {
1935  int p = evt.GetId() - ID_Triggers;
1936 
1937  mControls[p].mVal = mControls[p].mDef;
1938 }
1939 
1940 void LV2Effect::OnToggle(wxCommandEvent & evt)
1941 {
1942  int p = evt.GetId() - ID_Toggles;
1943 
1944  mControls[p].mVal = evt.GetInt() ? 1.0 : 0.0;
1945 }
1946 
1947 void LV2Effect::OnChoice(wxCommandEvent & evt)
1948 {
1949  int p = evt.GetId() - ID_Choices;
1950 
1951  mControls[p].mVal = mControls[p].mScaleValues[evt.GetInt()];
1952 }
1953 
1954 void LV2Effect::OnText(wxCommandEvent & evt)
1955 {
1956  int p = evt.GetId() - ID_Texts;
1957  LV2Port & ctrl = mControls[p];
1958 
1959  if (mParent->FindWindow(ID_Texts + p)->GetValidator()->TransferFromWindow())
1960  {
1961  ctrl.mVal = ctrl.mSampleRate ? ctrl.mTmp / mSampleRate : ctrl.mTmp;
1962 
1963  SetSlider(mSliders[p], mControls[p]);
1964  }
1965 }
1966 
1967 void LV2Effect::OnSlider(wxCommandEvent & evt)
1968 {
1969  int p = evt.GetId() - ID_Sliders;
1970  LV2Port & ctrl = mControls[p];
1971 
1972  float lo = ctrl.mLo;
1973  float hi = ctrl.mHi;
1974 
1975  if (ctrl.mLogarithmic)
1976  {
1977  lo = logf(lo);
1978  hi = logf(hi);
1979  }
1980 
1981  ctrl.mTmp = ((float) evt.GetInt() / 1000.0) * (hi - lo) + lo;
1982  ctrl.mTmp = ctrl.mLogarithmic ? expf(ctrl.mTmp) : ctrl.mTmp;
1983 
1984  ctrl.mTmp = ctrl.mTmp < ctrl.mLo ? ctrl.mLo : ctrl.mTmp;
1985  ctrl.mTmp = ctrl.mTmp > ctrl.mHi ? ctrl.mHi : ctrl.mTmp;
1986 
1987  ctrl.mVal = ctrl.mSampleRate ? ctrl.mTmp / mSampleRate : ctrl.mTmp;
1988 
1989  mParent->FindWindow(ID_Texts + p)->GetValidator()->TransferToWindow();
1990 }
1991 
1992 void LV2Effect::OnIdle(wxIdleEvent & WXUNUSED(evt))
1993 {
1994  if (mIdleFeature)
1995  {
1996  mIdleFeature->idle(suil_instance_get_handle(mSuilInstance));
1997  }
1998 }
1999 
2000 // ============================================================================
2001 // Feature handlers
2002 // ============================================================================
2003 
2004 // static callback
2005 uint32_t LV2Effect::uri_to_id(LV2_URI_Map_Callback_Data callback_data,
2006  const char *WXUNUSED(map),
2007  const char *uri)
2008 {
2009  return ((LV2Effect *)callback_data)->URID_Map(uri);
2010 }
2011 
2012 // static callback
2013 LV2_URID LV2Effect::urid_map(LV2_URID_Map_Handle handle, const char *uri)
2014 {
2015  return ((LV2Effect *)handle)->URID_Map(uri);
2016 }
2017 
2018 LV2_URID LV2Effect::URID_Map(const char *uri)
2019 {
2020  size_t ndx = mURIMap.size();
2021  for (size_t i = 0; i < ndx; i++)
2022  {
2023  if (strcmp(mURIMap[i].get(), uri) == 0)
2024  {
2025  return i + 1;
2026  }
2027  }
2028 
2029  mURIMap.resize(1 + mURIMap.size());
2030  mURIMap[ndx].reset( strdup(uri) );
2031 
2032  return ndx + 1;
2033 }
2034 
2035 // static callback
2036 const char *LV2Effect::urid_unmap(LV2_URID_Unmap_Handle handle, LV2_URID urid)
2037 {
2038  return ((LV2Effect *)handle)->URID_Unmap(urid);
2039 }
2040 
2041 const char *LV2Effect::URID_Unmap(LV2_URID urid)
2042 {
2043  if (urid > 0 && urid <= (LV2_URID) mURIMap.size())
2044  {
2045  return mURIMap[urid - 1].get();
2046  }
2047 
2048  return NULL;
2049 }
2050 
2051 // static callback
2052 int LV2Effect::ui_resize(LV2UI_Feature_Handle handle, int width, int height)
2053 {
2054  return ((LV2Effect *)handle)->UIResize(width, height);
2055 }
2056 
2057 int LV2Effect::UIResize(int WXUNUSED(width), int WXUNUSED(height))
2058 {
2059 #if 0
2060  // Nothing to do yet
2061 #endif
2062  return 1;
2063 }
2064 
2065 // static callback
2066 void LV2Effect::suil_write_func(SuilController controller,
2067  uint32_t port_index,
2068  uint32_t buffer_size,
2069  uint32_t protocol,
2070  const void *buffer)
2071 {
2072  ((LV2Effect *)controller)->UIWrite(port_index, buffer_size, protocol, buffer);
2073 }
2074 
2075 void LV2Effect::UIWrite(uint32_t port_index,
2076  uint32_t buffer_size,
2077  uint32_t protocol,
2078  const void *buffer)
2079 {
2080  if (protocol != 0 || buffer_size != sizeof(float))
2081  {
2082  return;
2083  }
2084 
2085  wxLongToLongHashMap::iterator it = mControlsMap.find(port_index);
2086  if (it != mControlsMap.end())
2087  {
2088  mControls[(it->second)].mVal = *((const float *)buffer);
2089  }
2090 }
2091 
2092 // static callback
2093 void LV2Effect::set_value_func(const char *port_symbol,
2094  void *user_data,
2095  const void *value,
2096  uint32_t size,
2097  uint32_t type)
2098 {
2099  ((LV2Effect *)user_data)->SetPortValue(port_symbol, value, size, type);
2100 }
2101 
2102 void LV2Effect::SetPortValue(const char *port_symbol,
2103  const void *value,
2104  uint32_t size,
2105  uint32_t type)
2106 {
2107  wxString symbol = wxString::FromUTF8(port_symbol);
2108  LV2_URID Bool = URID_Map(lilv_node_as_string(gBool));
2109  LV2_URID Double = URID_Map(lilv_node_as_string(gDouble));
2110  LV2_URID Float = URID_Map(lilv_node_as_string(gFloat));
2111  LV2_URID Int = URID_Map(lilv_node_as_string(gInt));
2112  LV2_URID Long = URID_Map(lilv_node_as_string(gLong));
2113 
2114  for (size_t p = 0, cnt = mControls.size(); p < cnt; p++)
2115  {
2116  if (mControls[p].mSymbol.IsSameAs(symbol))
2117  {
2118  if (type == Bool && size == sizeof(bool))
2119  {
2120  mControls[p].mVal = (float) *((const bool *) value) ? 1.0f : 0.0f;
2121  }
2122  else if (type == Double && size == sizeof(double))
2123  {
2124  mControls[p].mVal = (float) *((const double *) value);
2125  }
2126  else if (type == Float && size == sizeof(float))
2127  {
2128  mControls[p].mVal = (float) *((const float *) value);
2129  }
2130  else if (type == Int && size == sizeof(int32_t))
2131  {
2132  mControls[p].mVal = (float) *((const int32_t *) value);
2133  }
2134  else if (type == Long && size == sizeof(int64_t))
2135  {
2136  mControls[p].mVal = (float) *((const int64_t *) value);
2137  }
2138 
2139  break;
2140  }
2141  }
2142 }
2143 
2144 #endif
EVT_COMMAND_RANGE(ID_Slider, ID_Slider+NUMBER_OF_BANDS-1, wxEVT_COMMAND_SLIDER_UPDATED, EffectEqualization::OnSlider) EffectEqualization
#define WX_PIZZA(obj)
Definition: win_gtk.h:15
Derived from ShuttleGuiBase, an Audacity specific class for shuttling data to and from GUI...
Definition: ShuttleGui.h:366
void OnToggle(const CommandContext &context)
wxString label
Definition: Tags.cpp:727
#define URILIST
Definition: LoadLV2.h:38
Base class for many of the effects in Audacity.
Definition: Effect.h:61
#define XO(s)
Definition: Internat.h:30
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
#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
wxEVT_COMMAND_TEXT_UPDATED
Definition: Nyquist.cpp:109
EVT_BUTTON(wxID_NO, DependencyDialog::OnNo) EVT_BUTTON(wxID_YES
LilvWorld * gWorld
_("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 &)
void put(GtkWidget *widget, int x, int y, int width, int height)
ValueRestorer< T > valueRestorer(T &var)
Definition: MemoryX.h:875
wxCheckBox * TieCheckBox(const wxString &Prompt, WrappedType &WrappedRef)
const wxChar * name
Definition: Distortion.cpp:94
wxStaticText * AddVariableText(const wxString &Str, bool bCenter=false, int PositionFlags=0)
Definition: ShuttleGui.cpp:373
static wxString ToDisplayString(double numberToConvert, int digitsAfterDecimalPoint=-1)
Convert a number to a string, uses the user's locale's decimal separator.
Definition: Internat.cpp:148
wxStaticBox * StartStatic(const wxString &Str, int iProp=0)
Definition: ShuttleGui.cpp:701
Options & AutoPos(bool value)
std::unique_ptr< T, Destroyer< T >> Destroy_ptr
Definition: MemoryX.h:814
void AddStandardButtons(long buttons=eOkButton|eCancelButton, wxButton *extra=NULL)
END_EVENT_TABLE()
EffectDistortion::Params params
Definition: Distortion.cpp:95
void SetBorder(int Border)
Definition: ShuttleGui.h:251
#define lrintf(flt)
Definition: float_cast.h:137
void StartVerticalLay(int iProp=1)
Definition: ShuttleGui.cpp:982