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