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
13
14 #if defined(USE_LV2)
15
16 #if defined(__GNUC__)
17 #pragma GCC diagnostic ignored "-Wparentheses"
18 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
19 #elif defined(__clang__)
20 #pragma clang diagnostic ignored "-Wparentheses"
21 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
22 #endif
23
24 #include "LV2Effect.h"
25 #include "SampleCount.h"
26
27 #include <cmath>
28
29 #include <wx/button.h>
30 #include <wx/checkbox.h>
31 #include <wx/choice.h>
32 #include <wx/dcbuffer.h>
33 #include <wx/dialog.h>
34 #include <wx/crt.h>
35 #include <wx/log.h>
36 #include <wx/msgqueue.h>
37
38 #ifdef __WXMAC__
39 #include <wx/evtloop.h>
40 #endif
41
42 #include <wx/sizer.h>
43 #include <wx/slider.h>
44 #include <wx/statbox.h>
45 #include <wx/stattext.h>
46 #include <wx/tokenzr.h>
47 #include <wx/intl.h>
48 #include <wx/scrolwin.h>
49
50 #include "../../ShuttleGui.h"
51 #include "../../widgets/valnum.h"
52 #include "../../widgets/AudacityMessageBox.h"
53 #include "../../widgets/wxPanelWrapper.h"
54 #include "../../widgets/NumericTextCtrl.h"
55
56 #include "lilv/lilv.h"
57 #include "suil/suil.h"
58 #include "lv2/atom/atom.h"
59 #include "lv2/atom/forge.h"
60 #include "lv2/atom/util.h"
61 #include "lv2/instance-access/instance-access.h"
62 #include "lv2/port-groups/port-groups.h"
63 #include "lv2/parameters/parameters.h"
64 #include "lv2/state/state.h"
65 #include "lv2/ui/ui.h"
66
67 #if defined(__WXGTK__)
68 #include <gtk/gtk.h>
69 #endif
70
71 #if defined(__WXMSW__)
72 #include <wx/msw/wrapwin.h>
73 #endif
74
75 // Define a maximum block size in number of samples (not bytes)
76 #define DEFAULT_BLOCKSIZE 1048576
77
78 // Define a reasonable default sequence size in bytes
79 #define DEFAULT_SEQSIZE 8192
80
81 // Define the static URI map
82 URIDMap LV2Effect::gURIDMap;
83
84 // Define the static LILV URI nodes
85 #undef NODE
86 #define NODE(n, u) LilvNode *LV2Effect::node_##n = NULL;
87 NODELIST
88
89 // Define the static URIDs
90 #undef URID
91 #define URID(n, u) LV2_URID LV2Effect::urid_##n = 0;
92 URIDLIST
93
94 ///////////////////////////////////////////////////////////////////////////////
95 //
96 // LV2EffectMeter
97 //
98 ///////////////////////////////////////////////////////////////////////////////
99
100 class LV2EffectMeter final : public wxWindow
101 {
102 public:
103 LV2EffectMeter(wxWindow *parent, const LV2ControlPortPtr ctrl);
104 virtual ~LV2EffectMeter();
105
106 private:
107 void OnErase(wxEraseEvent &evt);
108 void OnPaint(wxPaintEvent &evt);
109 void OnIdle(wxIdleEvent &evt);
110 void OnSize(wxSizeEvent &evt);
111
112 private:
113 const LV2ControlPortPtr mControlPort;
114 float mLastValue;
115
116 DECLARE_EVENT_TABLE()
117 };
118
BEGIN_EVENT_TABLE(LV2EffectMeter,wxWindow)119 BEGIN_EVENT_TABLE(LV2EffectMeter, wxWindow)
120 EVT_IDLE(LV2EffectMeter::OnIdle)
121 EVT_ERASE_BACKGROUND(LV2EffectMeter::OnErase)
122 EVT_PAINT(LV2EffectMeter::OnPaint)
123 EVT_SIZE(LV2EffectMeter::OnSize)
124 END_EVENT_TABLE()
125
126 LV2EffectMeter::LV2EffectMeter(wxWindow *parent, const LV2ControlPortPtr port)
127 : wxWindow(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxDEFAULT_CONTROL_BORDER),
128 mControlPort(port)
129 {
130 mLastValue = -mControlPort->mVal;
131
132 SetBackgroundColour(*wxWHITE);
133 }
134
~LV2EffectMeter()135 LV2EffectMeter::~LV2EffectMeter()
136 {
137 }
138
OnIdle(wxIdleEvent & evt)139 void LV2EffectMeter::OnIdle(wxIdleEvent &evt)
140 {
141 evt.Skip();
142 if (mLastValue != mControlPort->mVal)
143 {
144 Refresh(false);
145 }
146 }
147
OnErase(wxEraseEvent & WXUNUSED (evt))148 void LV2EffectMeter::OnErase(wxEraseEvent &WXUNUSED(evt))
149 {
150 // Just ignore it to prevent flashing
151 }
152
OnPaint(wxPaintEvent & WXUNUSED (evt))153 void LV2EffectMeter::OnPaint(wxPaintEvent &WXUNUSED(evt))
154 {
155 std::unique_ptr<wxDC> dc {wxAutoBufferedPaintDCFactory(this)};
156
157 // Cache some metrics
158 wxRect r = GetClientRect();
159 wxCoord x = r.GetLeft();
160 wxCoord y = r.GetTop();
161 wxCoord w = r.GetWidth();
162 wxCoord h = r.GetHeight();
163
164 // These use unscaled value, min, and max
165 float val = mControlPort->mVal;
166 if (val > mControlPort->mMax)
167 {
168 val = mControlPort->mMax;
169 }
170 if (val < mControlPort->mMin)
171 {
172 val = mControlPort->mMin;
173 }
174 val -= mControlPort->mMin;
175
176 // Setup for erasing the background
177 dc->SetPen(*wxTRANSPARENT_PEN);
178 dc->SetBrush(wxColour(100, 100, 220));
179
180 dc->Clear();
181 dc->DrawRectangle(x, y, (w * (val / fabs(mControlPort->mMax - mControlPort->mMin))), h);
182
183 mLastValue = mControlPort->mVal;
184 }
185
OnSize(wxSizeEvent & WXUNUSED (evt))186 void LV2EffectMeter::OnSize(wxSizeEvent &WXUNUSED(evt))
187 {
188 Refresh(false);
189 }
190
191 ///////////////////////////////////////////////////////////////////////////////
192 //
193 // LV2EffectSettingsDialog
194 //
195 ///////////////////////////////////////////////////////////////////////////////
196
197 class LV2EffectSettingsDialog final : public wxDialogWrapper
198 {
199 public:
200 LV2EffectSettingsDialog(wxWindow *parent, LV2Effect *effect);
201 virtual ~LV2EffectSettingsDialog();
202
203 void PopulateOrExchange(ShuttleGui &S);
204
205 void OnOk(wxCommandEvent &evt);
206
207 private:
208 LV2Effect *mEffect;
209 int mBufferSize;
210 bool mUseLatency;
211 bool mUseGUI;
212
213 DECLARE_EVENT_TABLE()
214 };
215
BEGIN_EVENT_TABLE(LV2EffectSettingsDialog,wxDialogWrapper)216 BEGIN_EVENT_TABLE(LV2EffectSettingsDialog, wxDialogWrapper)
217 EVT_BUTTON(wxID_OK, LV2EffectSettingsDialog::OnOk)
218 END_EVENT_TABLE()
219
220 LV2EffectSettingsDialog::LV2EffectSettingsDialog(wxWindow *parent, LV2Effect *effect)
221 : wxDialogWrapper(parent, wxID_ANY, XO("LV2 Effect Settings"))
222 {
223 mEffect = effect;
224
225 mEffect->mHost->GetSharedConfig(wxT("Settings"), wxT("BufferSize"), mBufferSize, 8192);
226 mEffect->mHost->GetSharedConfig(wxT("Settings"), wxT("UseLatency"), mUseLatency, true);
227 mEffect->mHost->GetSharedConfig(wxT("Settings"), wxT("UseGUI"), mUseGUI, true);
228
229 ShuttleGui S(this, eIsCreating);
230 PopulateOrExchange(S);
231 }
232
~LV2EffectSettingsDialog()233 LV2EffectSettingsDialog::~LV2EffectSettingsDialog()
234 {
235 }
236
PopulateOrExchange(ShuttleGui & S)237 void LV2EffectSettingsDialog::PopulateOrExchange(ShuttleGui &S)
238 {
239 S.SetBorder(5);
240 S.StartHorizontalLay(wxEXPAND, 1);
241 {
242 S.StartVerticalLay(false);
243 {
244 // This really shouldn't be required for LV2 plugins because they have the ability
245 // to specify their exact requirements in the TTL file and/or to check the host
246 // supplied min/max values. However, I've run across one (Harrison Consoles XT-EQ)
247 // that crashed on sizes greater than 8192.
248 S.StartStatic(XO("Buffer Size"));
249 {
250 IntegerValidator<int> vld(&mBufferSize);
251 vld.SetRange(8, DEFAULT_BLOCKSIZE);
252
253 S.AddVariableText( XO(
254 "The buffer size controls the number of samples sent to the effect "
255 "on each iteration. Smaller values will cause slower processing and "
256 "some effects require 8192 samples or less to work properly. However "
257 "most effects can accept large buffers and using them will greatly "
258 "reduce processing time."),
259 false, 0, 650);
260
261 S.StartHorizontalLay(wxALIGN_LEFT);
262 {
263 wxTextCtrl *t;
264 t = S.TieNumericTextBox(
265 XXO("&Buffer Size (8 to %d) samples:")
266 .Format( DEFAULT_BLOCKSIZE ),
267 mBufferSize,
268 12);
269 t->SetMinSize(wxSize(100, -1));
270 t->SetValidator(vld);
271 }
272 S.EndHorizontalLay();
273 }
274 S.EndStatic();
275
276 S.StartStatic(XO("Latency Compensation"));
277 {
278 S.AddVariableText( XO(
279 "As part of their processing, some LV2 effects must delay returning "
280 "audio to Audacity. When not compensating for this delay, you will "
281 "notice that small silences have been inserted into the audio. "
282 "Enabling this setting will provide that compensation, but it may "
283 "not work for all LV2 effects."),
284 false, 0, 650);
285
286 S.StartHorizontalLay(wxALIGN_LEFT);
287 {
288 S.TieCheckBox(XXO("Enable &compensation"),
289 mUseLatency);
290 }
291 S.EndHorizontalLay();
292 }
293 S.EndStatic();
294
295 S.StartStatic(XO("Graphical Mode"));
296 {
297 S.AddVariableText( XO(
298 "LV2 effects can have a graphical interface for setting parameter values."
299 " A basic text-only method is also available. "
300 " Reopen the effect for this to take effect."),
301 false, 0, 650);
302 S.TieCheckBox(XXO("Enable &graphical interface"),
303 mUseGUI);
304 }
305 S.EndStatic();
306 }
307 S.EndVerticalLay();
308 }
309 S.EndHorizontalLay();
310
311 S.AddStandardButtons();
312
313 Layout();
314 Fit();
315 Center();
316 }
317
OnOk(wxCommandEvent & WXUNUSED (evt))318 void LV2EffectSettingsDialog::OnOk(wxCommandEvent &WXUNUSED(evt))
319 {
320 if (!Validate())
321 {
322 return;
323 }
324
325 ShuttleGui S(this, eIsGettingFromDialog);
326 PopulateOrExchange(S);
327
328 mEffect->mHost->SetSharedConfig(wxT("Settings"), wxT("BufferSize"), mBufferSize);
329 mEffect->mHost->SetSharedConfig(wxT("Settings"), wxT("UseLatency"), mUseLatency);
330 mEffect->mHost->SetSharedConfig(wxT("Settings"), wxT("UseGUI"), mUseGUI);
331
332 EndModal(wxID_OK);
333 }
334
335 ///////////////////////////////////////////////////////////////////////////////
336 //
337 // LV2Effect
338 //
339 ///////////////////////////////////////////////////////////////////////////////
340
341 enum
342 {
343 ID_Duration = 10000,
344 ID_Triggers = 11000,
345 ID_Toggles = 12000,
346 ID_Sliders = 13000,
347 ID_Choices = 14000,
348 ID_Texts = 15000,
349 ID_TIMER = 20000,
350 };
351
BEGIN_EVENT_TABLE(LV2Effect,wxEvtHandler)352 BEGIN_EVENT_TABLE(LV2Effect, wxEvtHandler)
353 EVT_COMMAND_RANGE(ID_Triggers, ID_Triggers + 999, wxEVT_COMMAND_BUTTON_CLICKED, LV2Effect::OnTrigger)
354 EVT_COMMAND_RANGE(ID_Toggles, ID_Toggles + 999, wxEVT_COMMAND_CHECKBOX_CLICKED, LV2Effect::OnToggle)
355 EVT_COMMAND_RANGE(ID_Sliders, ID_Sliders + 999, wxEVT_COMMAND_SLIDER_UPDATED, LV2Effect::OnSlider)
356 EVT_COMMAND_RANGE(ID_Choices, ID_Choices + 999, wxEVT_COMMAND_CHOICE_SELECTED, LV2Effect::OnChoice)
357 EVT_COMMAND_RANGE(ID_Texts, ID_Texts + 999, wxEVT_COMMAND_TEXT_UPDATED, LV2Effect::OnText)
358
359 EVT_TIMER(ID_TIMER, LV2Effect::OnTimer)
360 EVT_IDLE(LV2Effect::OnIdle)
361 END_EVENT_TABLE()
362
363 LV2Effect::LV2Effect(const LilvPlugin *plug)
364 {
365 mPlug = plug;
366
367 mHost = NULL;
368 mMaster = NULL;
369 mProcess = NULL;
370 mSuilInstance = NULL;
371
372 mSampleRate = 44100;
373 mBlockSize = DEFAULT_BLOCKSIZE;
374 mSeqSize = DEFAULT_SEQSIZE;
375
376 mMinBlockSize = 1;
377 mMaxBlockSize = mBlockSize;
378 mUserBlockSize = mBlockSize;
379
380 mLatencyPort = -1;
381 mLatencyDone = false;
382 mRolling = false;
383 mActivated = false;
384
385 mDialog = NULL;
386
387 mUIIdleInterface = NULL;
388 mUIShowInterface = NULL;
389
390 mAudioIn = 0;
391 mAudioOut = 0;
392 mMidiIn = 0;
393 mMidiOut = 0;
394
395 mControlIn.reset();
396 mControlOut.reset();
397
398 mPositionSpeed = 1.0;
399 mPositionFrame = 0.0;
400
401 mNativeWin = NULL;
402 mNativeWinInitialSize = wxDefaultSize;
403 mNativeWinLastSize = wxDefaultSize;
404 mResizing = false;
405 #if defined(__WXGTK__)
406 mResized = false;
407 #endif
408
409 mExternalUIHost.plugin_human_id = NULL;
410 mExternalWidget = NULL;
411 mExternalUIClosed = false;
412
413 mNoResize = false;
414
415 mSupportsNominalBlockLength = false;
416 mSupportsSampleRate = false;
417
418 mFactoryPresetsLoaded = false;
419 }
420
~LV2Effect()421 LV2Effect::~LV2Effect()
422 {
423 }
424
425 // ============================================================================
426 // ComponentInterface Implementation
427 // ============================================================================
428
GetPath()429 PluginPath LV2Effect::GetPath()
430 {
431 return LilvString(lilv_plugin_get_uri(mPlug));
432 }
433
GetSymbol()434 ComponentInterfaceSymbol LV2Effect::GetSymbol()
435 {
436 return LilvString(lilv_plugin_get_name(mPlug), true);
437 }
438
GetVendor()439 VendorSymbol LV2Effect::GetVendor()
440 {
441 wxString vendor = LilvString(lilv_plugin_get_author_name(mPlug), true);
442
443 if (vendor.empty())
444 {
445 return XO("n/a");
446 }
447
448 return {vendor};
449 }
450
GetVersion()451 wxString LV2Effect::GetVersion()
452 {
453 return wxT("1.0");
454 }
455
GetDescription()456 TranslatableString LV2Effect::GetDescription()
457 {
458 return XO("n/a");
459 }
460
461 // ============================================================================
462 // EffectDefinitionInterface Implementation
463 // ============================================================================
464
GetType()465 EffectType LV2Effect::GetType()
466 {
467 if (GetAudioInCount() == 0 && GetAudioOutCount() == 0)
468 {
469 return EffectTypeTool;
470 }
471
472 if (GetAudioInCount() == 0)
473 {
474 return EffectTypeGenerate;
475 }
476
477 if (GetAudioOutCount() == 0)
478 {
479 return EffectTypeAnalyze;
480 }
481
482 return EffectTypeProcess;
483 }
484
GetFamily()485 EffectFamilySymbol LV2Effect::GetFamily()
486 {
487 return LV2EFFECTS_FAMILY;
488 }
489
IsInteractive()490 bool LV2Effect::IsInteractive()
491 {
492 return mControlPorts.size() != 0;
493 }
494
IsDefault()495 bool LV2Effect::IsDefault()
496 {
497 return false;
498 }
499
IsLegacy()500 bool LV2Effect::IsLegacy()
501 {
502 return false;
503 }
504
SupportsRealtime()505 bool LV2Effect::SupportsRealtime()
506 {
507 return GetType() == EffectTypeProcess;
508 }
509
SupportsAutomation()510 bool LV2Effect::SupportsAutomation()
511 {
512 return true;
513 }
514
515 // ============================================================================
516 // EffectClientInterface Implementation
517 // ============================================================================
SetHost(EffectHostInterface * host)518 bool LV2Effect::SetHost(EffectHostInterface *host)
519 {
520 mHost = host;
521
522 AddOption(urid_SequenceSize, sizeof(mSeqSize), urid_Int, &mSeqSize);
523 AddOption(urid_MinBlockLength, sizeof(mMinBlockSize), urid_Int, &mMinBlockSize);
524 AddOption(urid_MaxBlockLength, sizeof(mMaxBlockSize), urid_Int, &mMaxBlockSize);
525
526 mBlockSizeOption = AddOption(urid_NominalBlockLength,
527 sizeof(mBlockSize),
528 urid_Int,
529 &mBlockSize);
530 mSampleRateOption = AddOption(urid_SampleRate,
531 sizeof(mSampleRate),
532 urid_Float,
533 &mSampleRate);
534 AddOption(0, 0, 0, NULL);
535
536 if (!ValidateOptions(lilv_plugin_get_uri(mPlug)))
537 {
538 return false;
539 }
540
541 mUriMapFeature.callback_data = this;
542 mUriMapFeature.uri_to_id = LV2Effect::uri_to_id;
543
544 mURIDMapFeature.handle = this;
545 mURIDMapFeature.map = LV2Effect::urid_map;
546
547 mURIDUnmapFeature.handle = this;
548 mURIDUnmapFeature.unmap = LV2Effect::urid_unmap;
549
550 mUIResizeFeature.handle = this;
551 mUIResizeFeature.ui_resize = LV2Effect::ui_resize;
552
553 mLogFeature.handle = this;
554 mLogFeature.printf = LV2Effect::log_printf;
555 mLogFeature.vprintf = LV2Effect::log_vprintf;
556
557 mExternalUIHost.ui_closed = LV2Effect::ui_closed;
558
559 LilvNode *pluginName = lilv_plugin_get_name(mPlug);
560 mExternalUIHost.plugin_human_id = lilv_node_as_string(pluginName);
561 lilv_node_free(pluginName);
562
563 AddFeature(LV2_UI__noUserResize, NULL);
564 AddFeature(LV2_UI__fixedSize, NULL);
565 AddFeature(LV2_UI__idleInterface, NULL);
566 AddFeature(LV2_UI__makeResident, NULL);
567 AddFeature(LV2_BUF_SIZE__boundedBlockLength, NULL);
568 AddFeature(LV2_BUF_SIZE__fixedBlockLength, NULL);
569 AddFeature(LV2_OPTIONS__options, mOptions.data());
570 AddFeature(LV2_URI_MAP_URI, &mUriMapFeature);
571 AddFeature(LV2_URID__map, &mURIDMapFeature);
572 AddFeature(LV2_URID__unmap, &mURIDUnmapFeature);
573 AddFeature(LV2_UI__resize, &mUIResizeFeature);
574 AddFeature(LV2_DATA_ACCESS_URI, &mExtensionDataFeature);
575 AddFeature(LV2_LOG__log, &mLogFeature);
576 AddFeature(LV2_EXTERNAL_UI__Host, &mExternalUIHost);
577 AddFeature(LV2_EXTERNAL_UI_DEPRECATED_URI, &mExternalUIHost);
578 // Some plugins specify this as a feature
579 AddFeature(LV2_EXTERNAL_UI__Widget, NULL);
580
581 mInstanceAccessFeature = AddFeature(LV2_INSTANCE_ACCESS_URI, NULL);
582 mParentFeature = AddFeature(LV2_UI__parent, NULL);
583
584 AddFeature(NULL, NULL);
585
586 if (!ValidateFeatures(lilv_plugin_get_uri(mPlug)))
587 {
588 return false;
589 }
590
591 auto minLength = lilv_world_get(gWorld, lilv_plugin_get_uri(mPlug), node_MinBlockLength, NULL);
592 if (minLength)
593 {
594 if (lilv_node_is_int(minLength))
595 {
596 int val = lilv_node_as_int(minLength);
597 if (mMinBlockSize < val)
598 {
599 mMinBlockSize = val;
600 }
601 }
602 lilv_node_free(minLength);
603 }
604
605 auto maxLength = lilv_world_get(gWorld, lilv_plugin_get_uri(mPlug), node_MaxBlockLength, NULL);
606 if (maxLength)
607 {
608 if (lilv_node_is_int(maxLength))
609 {
610 int val = lilv_node_as_int(maxLength);
611 if (mMaxBlockSize > val)
612 {
613 mMaxBlockSize = val;
614 }
615 }
616 lilv_node_free(maxLength);
617 }
618
619 if (mMinBlockSize > mMaxBlockSize)
620 {
621 mMaxBlockSize = mMinBlockSize;
622 }
623
624 auto numPorts = lilv_plugin_get_num_ports(mPlug);
625
626 // Allocate buffers for the port indices and the default control values
627 Floats minimumVals {numPorts};
628 Floats maximumVals {numPorts};
629 Floats defaultVals {numPorts};
630
631 // Retrieve the port ranges for all ports (some values may be NaN)
632 lilv_plugin_get_port_ranges_float(mPlug,
633 minimumVals.get(),
634 maximumVals.get(),
635 defaultVals.get());
636
637 // Get info about all ports
638 for (size_t i = 0; i < numPorts; i++)
639 {
640 const LilvPort *port = lilv_plugin_get_port_by_index(mPlug, i);
641 int index = lilv_port_get_index(mPlug, port);
642
643 // It must be input or output, anything else is bogus
644 bool isInput;
645 if (lilv_port_is_a(mPlug, port, node_InputPort))
646 {
647 isInput = true;
648 }
649 else if (lilv_port_is_a(mPlug, port, node_OutputPort))
650 {
651 isInput = false;
652 }
653 else
654 {
655 assert(false);
656 return false;
657 }
658
659 // Get the port name and symbol
660 wxString symbol = LilvString(lilv_port_get_symbol(mPlug, port));
661 wxString name = LilvString(lilv_port_get_name(mPlug, port), true);
662
663 // Get the group to which this port belongs or default to the main group
664 wxString groupName = wxEmptyString;
665 LilvNode *group = lilv_port_get(mPlug, port, node_Group);
666 if (group)
667 {
668 groupName = LilvString(lilv_world_get(gWorld, group, node_Label, NULL), true);
669 if (groupName.empty())
670 {
671 groupName = LilvString(lilv_world_get(gWorld, group, node_Name, NULL), true);
672 }
673
674 if (groupName.empty())
675 {
676 groupName = LilvString(group);
677 }
678
679 lilv_node_free(group);
680 }
681 else
682 {
683 groupName = _("Effect Settings");
684 }
685
686 // Get the latency port
687 uint32_t latencyIndex = lilv_plugin_get_latency_port_index(mPlug);
688
689 // Get the ports designation (must be freed)
690 LilvNode *designation = lilv_port_get(mPlug, port, node_Designation);
691
692 // Check for audio ports
693 if (lilv_port_is_a(mPlug, port, node_AudioPort))
694 {
695 mAudioPorts.push_back(std::make_shared<LV2AudioPort>(port, index, isInput, symbol, name, groupName));
696
697 isInput ? mAudioIn++ : mAudioOut++;
698 }
699 // Check for Control ports
700 else if (lilv_port_is_a(mPlug, port, node_ControlPort))
701 {
702 // Add group if not previously done
703 if (mGroupMap.find(groupName) == mGroupMap.end())
704 {
705 mGroups.push_back(groupName);
706 }
707 mGroupMap[groupName].push_back(mControlPorts.size());
708
709 mControlPorts.push_back(std::make_shared<LV2ControlPort>(port, index, isInput, symbol, name, groupName));
710 LV2ControlPortPtr controlPort = mControlPorts.back();
711
712 // Get any unit descriptor
713 LilvNode *unit = lilv_port_get(mPlug, port, node_Unit);
714 if (unit)
715 {
716 // Really should use lilv_world_get_symbol()
717 LilvNode *symbol = lilv_world_get_symbol(gWorld, unit);
718 if (symbol)
719 {
720 controlPort->mUnits = LilvString(symbol);
721 lilv_node_free(symbol);
722 }
723 lilv_node_free(unit);
724 }
725
726 // Get the scale points
727 LilvScalePoints *points = lilv_port_get_scale_points(mPlug, port);
728 LILV_FOREACH(scale_points, j, points)
729 {
730 const LilvScalePoint *point = lilv_scale_points_get(points, j);
731
732 controlPort->mScaleValues.push_back(lilv_node_as_float(lilv_scale_point_get_value(point)));
733 controlPort->mScaleLabels.push_back(LilvString(lilv_scale_point_get_label(point)));
734 }
735 lilv_scale_points_free(points);
736
737 // Collect the value and range info
738 controlPort->mHasLo = !std::isnan(minimumVals[i]);
739 controlPort->mHasHi = !std::isnan(maximumVals[i]);
740 controlPort->mMin = controlPort->mHasLo ? minimumVals[i] : 0.0;
741 controlPort->mMax = controlPort->mHasHi ? maximumVals[i] : 1.0;
742 controlPort->mLo = controlPort->mMin;
743 controlPort->mHi = controlPort->mMax;
744 controlPort->mDef = !std::isnan(defaultVals[i])
745 ? defaultVals[i]
746 : controlPort->mHasLo
747 ? controlPort->mLo
748 : controlPort->mHasHi
749 ? controlPort->mHi
750 : 0.0;
751 controlPort->mVal = controlPort->mDef;
752 controlPort->mLst = controlPort->mVal;
753
754 // Figure out the type of port we have
755 if (isInput)
756 {
757 if (lilv_port_has_property(mPlug, port, node_Toggled))
758 {
759 controlPort->mToggle = true;
760 }
761 else if (lilv_port_has_property(mPlug, port, node_Enumeration))
762 {
763 controlPort->mEnumeration = true;
764 }
765 else if (lilv_port_has_property(mPlug, port, node_Integer))
766 {
767 controlPort->mInteger = true;
768 }
769 else if (lilv_port_has_property(mPlug, port, node_SampleRate))
770 {
771 controlPort->mSampleRate = true;
772 }
773
774 // Trigger properties can be combined with other types, but it
775 // seems mostly to be combined with toggle. So, we turn the
776 // checkbox into a button.
777 if (lilv_port_has_property(mPlug, port, node_Trigger))
778 {
779 controlPort->mTrigger = true;
780 }
781
782 // We'll make the slider logarithmic
783 if (lilv_port_has_property(mPlug, port, node_Logarithmic))
784 {
785 controlPort->mLogarithmic = true;
786 }
787
788 if (lilv_port_has_property(mPlug, port, node_Enumeration))
789 {
790 controlPort->mEnumeration = true;
791 }
792
793 mControlPortMap[controlPort->mIndex] = controlPort;
794 }
795 else
796 {
797 if (controlPort->mIndex == latencyIndex)
798 {
799 mLatencyPort = i;
800 }
801 }
802 }
803 // Check for atom ports
804 else if (lilv_port_is_a(mPlug, port, node_AtomPort))
805 {
806 mAtomPorts.push_back(std::make_shared<LV2AtomPort>(port, index, isInput, symbol, name, groupName));
807 std::shared_ptr<LV2AtomPort> atomPort = mAtomPorts.back();
808
809 atomPort->mMinimumSize = 8192;
810 LilvNode *min = lilv_port_get(mPlug, port, node_MinimumSize);
811 if (min)
812 {
813 if (lilv_node_is_int(min))
814 {
815 uint32_t val = lilv_node_as_int(min);
816 if (atomPort->mMinimumSize < val)
817 {
818 atomPort->mMinimumSize = val;
819 }
820 }
821 lilv_node_free(min);
822 }
823
824 atomPort->mBuffer.resize(atomPort->mMinimumSize);
825 atomPort->mRing = zix_ring_new(atomPort->mMinimumSize);
826 zix_ring_mlock(atomPort->mRing);
827
828 if (lilv_port_supports_event(mPlug, port, node_Position))
829 {
830 atomPort->mWantsPosition = true;
831 }
832
833 if (lilv_port_supports_event(mPlug, port, node_MidiEvent))
834 {
835 atomPort->mIsMidi = true;
836 (isInput ? mMidiIn : mMidiOut) += 1;
837 }
838
839 bool isControl = lilv_node_equals(designation, node_Control);
840 if (isInput)
841 {
842 if (!mControlIn || isControl)
843 {
844 mControlIn = atomPort;
845 }
846 }
847 else
848 {
849 if (!mControlOut || isControl)
850 {
851 mControlOut = atomPort;
852 }
853 }
854 }
855 // Check for CV ports
856 else if (lilv_port_is_a(mPlug, port, node_CVPort))
857 {
858 mCVPorts.push_back(std::make_shared<LV2CVPort>(port, index, isInput, symbol, name, groupName));
859 std::shared_ptr<LV2CVPort> cvPort = mCVPorts.back();
860
861 // Collect the value and range info
862 if (!std::isnan(minimumVals[i]))
863 {
864 cvPort->mHasLo = true;
865 cvPort->mMin = minimumVals[i];
866 }
867
868 if (!std::isnan(maximumVals[i]))
869 {
870 cvPort->mHasHi = true;
871 cvPort->mMax = maximumVals[i];
872 }
873
874 if (!std::isnan(defaultVals[i]))
875 {
876 cvPort->mDef = defaultVals[i];
877 }
878 else if (cvPort->mHasLo)
879 {
880 cvPort->mDef = cvPort->mMin;
881 }
882 else if (cvPort->mHasHi)
883 {
884 cvPort->mDef = cvPort->mMax;
885 }
886 }
887
888 // Free the designation node
889 if (designation)
890 {
891 lilv_node_free(designation);
892 }
893 }
894
895 // Ignore control designation if one of them is missing
896 if ((mControlIn && !mControlOut) || (!mControlIn && mControlOut))
897 {
898 mControlIn.reset();
899 mControlOut.reset();
900 }
901
902 // Determine available extensions
903 mWantsOptionsInterface = false;
904 mWantsWorkerInterface = false;
905 mWantsStateInterface = false;
906
907 LilvNodes *extdata = lilv_plugin_get_extension_data(mPlug);
908 if (extdata)
909 {
910 LILV_FOREACH(nodes, i, extdata)
911 {
912 const LilvNode *node = lilv_nodes_get(extdata, i);
913 const char *uri = lilv_node_as_string(node);
914
915 if (strcmp(uri, LV2_OPTIONS__interface) == 0)
916 {
917 mWantsOptionsInterface = true;
918 }
919 else if (strcmp(uri, LV2_WORKER__interface) == 0)
920 {
921 mWantsWorkerInterface = true;
922 }
923 else if (strcmp(uri, LV2_STATE__interface) == 0)
924 {
925 mWantsStateInterface = true;
926 }
927 }
928 lilv_nodes_free(extdata);
929 }
930
931 // mHost will be null during registration
932 if (mHost)
933 {
934 int userBlockSize;
935 mHost->GetSharedConfig(wxT("Settings"), wxT("BufferSize"), userBlockSize, 8192);
936 mUserBlockSize = std::max(1, userBlockSize);
937 mHost->GetSharedConfig(wxT("Settings"), wxT("UseLatency"), mUseLatency, true);
938 mHost->GetSharedConfig(wxT("Settings"), wxT("UseGUI"), mUseGUI, true);
939
940 mBlockSize = mUserBlockSize;
941
942 bool haveDefaults;
943 mHost->GetPrivateConfig(mHost->GetFactoryDefaultsGroup(), wxT("Initialized"), haveDefaults, false);
944 if (!haveDefaults)
945 {
946 SaveParameters(mHost->GetFactoryDefaultsGroup());
947 mHost->SetPrivateConfig(mHost->GetFactoryDefaultsGroup(), wxT("Initialized"), true);
948 }
949
950 LoadParameters(mHost->GetCurrentSettingsGroup());
951 }
952
953 lv2_atom_forge_init(&mForge, &mURIDMapFeature);
954
955 return true;
956 }
957
GetAudioInCount()958 unsigned LV2Effect::GetAudioInCount()
959 {
960 return mAudioIn;
961 }
962
GetAudioOutCount()963 unsigned LV2Effect::GetAudioOutCount()
964 {
965 return mAudioOut;
966 }
967
GetMidiInCount()968 int LV2Effect::GetMidiInCount()
969 {
970 return mMidiIn;
971 }
972
GetMidiOutCount()973 int LV2Effect::GetMidiOutCount()
974 {
975 return mMidiOut;
976 }
977
SetSampleRate(double rate)978 void LV2Effect::SetSampleRate(double rate)
979 {
980 mSampleRate = (float) rate;
981
982 if (mMaster)
983 {
984 mMaster->SetSampleRate();
985 }
986
987 for (size_t i = 0, cnt = mSlaves.size(); i < cnt; i++)
988 {
989 mSlaves[i]->SetSampleRate();
990 }
991 }
992
SetBlockSize(size_t maxBlockSize)993 size_t LV2Effect::SetBlockSize(size_t maxBlockSize)
994 {
995 mBlockSize = std::min(std::min((int)maxBlockSize, mUserBlockSize), mMaxBlockSize);
996
997 if (mBlockSize < mMinBlockSize)
998 {
999 mBlockSize = mMinBlockSize;
1000 }
1001 if (mBlockSize > mMaxBlockSize)
1002 {
1003 mBlockSize = mMaxBlockSize;
1004 }
1005
1006 if (mMaster)
1007 {
1008 mMaster->SetBlockSize();
1009 }
1010
1011 for (size_t i = 0, cnt = mSlaves.size(); i < cnt; i++)
1012 {
1013 mSlaves[i]->SetBlockSize();
1014 }
1015
1016 return mBlockSize;
1017 }
1018
GetBlockSize() const1019 size_t LV2Effect::GetBlockSize() const
1020 {
1021 return mBlockSize;
1022 }
1023
GetLatency()1024 sampleCount LV2Effect::GetLatency()
1025 {
1026 if (mUseLatency && mLatencyPort >= 0 && !mLatencyDone)
1027 {
1028 mLatencyDone = true;
1029 return sampleCount(mMaster->GetLatency());
1030 }
1031
1032 return 0;
1033 }
1034
GetTailSize()1035 size_t LV2Effect::GetTailSize()
1036 {
1037 return 0;
1038 }
1039
IsReady()1040 bool LV2Effect::IsReady()
1041 {
1042 return mMaster != NULL;
1043 }
1044
ProcessInitialize(sampleCount WXUNUSED (totalLen),ChannelNames WXUNUSED (chanMap))1045 bool LV2Effect::ProcessInitialize(sampleCount WXUNUSED(totalLen), ChannelNames WXUNUSED(chanMap))
1046 {
1047 mProcess = InitInstance(mSampleRate);
1048 if (!mProcess)
1049 {
1050 return false;
1051 }
1052
1053 for (auto & port : mCVPorts)
1054 {
1055 port->mBuffer.reinit((unsigned) mBlockSize, port->mIsInput);
1056 }
1057
1058 lilv_instance_activate(mProcess->GetInstance());
1059 mActivated = true;
1060
1061 mLatencyDone = false;
1062
1063 return true;
1064 }
1065
ProcessFinalize()1066 bool LV2Effect::ProcessFinalize()
1067 {
1068 if (mProcess)
1069 {
1070 FreeInstance(mProcess);
1071 mProcess = NULL;
1072 }
1073
1074 return true;
1075 }
1076
ProcessBlock(float ** inbuf,float ** outbuf,size_t size)1077 size_t LV2Effect::ProcessBlock(float **inbuf, float **outbuf, size_t size)
1078 {
1079 wxASSERT(size <= ( size_t) mBlockSize);
1080
1081 LilvInstance *instance = mProcess->GetInstance();
1082
1083 int i = 0;
1084 int o = 0;
1085 for (auto & port : mAudioPorts)
1086 {
1087 lilv_instance_connect_port(instance,
1088 port->mIndex,
1089 (port->mIsInput ? inbuf[i++] : outbuf[o++]));
1090 }
1091
1092 // Transfer incoming events from the ring buffer to the event buffer for each
1093 // atom input port. These will be made available to each slave in the chain and
1094 // to the master once all slaves have run.
1095 //
1096 // In addition, reset the output Atom ports.
1097 for (auto & port : mAtomPorts)
1098 {
1099 uint8_t *buf = port->mBuffer.data();
1100
1101 if (port->mIsInput)
1102 {
1103 lv2_atom_forge_set_buffer(&mForge,
1104 buf,
1105 port->mBuffer.size());
1106
1107 LV2_Atom_Forge_Frame seqFrame;
1108 LV2_Atom_Sequence *seq = ( LV2_Atom_Sequence *)
1109 lv2_atom_forge_sequence_head(&mForge, &seqFrame, 0);
1110
1111 if (port->mWantsPosition)
1112 {
1113 lv2_atom_forge_frame_time(&mForge, mPositionFrame);
1114
1115 LV2_Atom_Forge_Frame posFrame;
1116 lv2_atom_forge_object(&mForge, &posFrame, 0, urid_Position);
1117 lv2_atom_forge_key(&mForge, urid_Speed);
1118 lv2_atom_forge_float(&mForge, mPositionSpeed);
1119 lv2_atom_forge_key(&mForge, urid_Frame);
1120 lv2_atom_forge_long(&mForge, mPositionFrame);
1121 lv2_atom_forge_pop(&mForge, &posFrame);
1122 }
1123
1124 ZixRing *ring = port->mRing;
1125 LV2_Atom atom;
1126 while (zix_ring_read(ring, &atom, sizeof(atom)))
1127 {
1128 if (mForge.offset + sizeof(LV2_Atom_Event) + atom.size < mForge.size)
1129 {
1130 lv2_atom_forge_frame_time(&mForge, mPositionFrame);
1131
1132 lv2_atom_forge_write(&mForge, &atom, sizeof(atom));
1133 zix_ring_read(ring, &mForge.buf[mForge.offset], atom.size);
1134 mForge.offset += atom.size;
1135 seq->atom.size += atom.size;
1136 }
1137 else
1138 {
1139 zix_ring_skip(ring, atom.size);
1140 wxLogError(wxT("LV2 sequence buffer overflow"));
1141 }
1142 }
1143
1144 lv2_atom_forge_pop(&mForge, &seqFrame);
1145 }
1146 else
1147 {
1148 port->mBuffer.resize(port->mMinimumSize);
1149 *(( LV2_Atom *) buf) =
1150 {
1151 port->mMinimumSize,
1152 urid_Chunk
1153 };
1154 }
1155 }
1156
1157 lilv_instance_run(instance, size);
1158
1159 mProcess->SendResponses();
1160
1161 for (auto & port : mAtomPorts)
1162 {
1163 if (!port->mIsInput)
1164 {
1165 port->mBuffer.resize(port->mMinimumSize);
1166
1167 LV2_Atom *chunk = ( LV2_Atom *) port->mBuffer.data();
1168 chunk->size = port->mMinimumSize;
1169 chunk->type = urid_Chunk;
1170 }
1171 }
1172
1173 return size;
1174 }
1175
RealtimeInitialize()1176 bool LV2Effect::RealtimeInitialize()
1177 {
1178 mMasterIn.reinit(mAudioIn, (unsigned int) mBlockSize);
1179 mMasterOut.reinit(mAudioOut, (unsigned int) mBlockSize);
1180
1181 for (auto & port : mCVPorts)
1182 {
1183 port->mBuffer.reinit((unsigned) mBlockSize, port->mIsInput);
1184 }
1185
1186 lilv_instance_activate(mMaster->GetInstance());
1187 mActivated = true;
1188
1189 return true;
1190 }
1191
RealtimeFinalize()1192 bool LV2Effect::RealtimeFinalize()
1193 {
1194 for (auto & slave : mSlaves)
1195 {
1196 FreeInstance(slave);
1197 }
1198 mSlaves.clear();
1199
1200 if (mActivated)
1201 {
1202 lilv_instance_deactivate(mMaster->GetInstance());
1203 mActivated = false;
1204 }
1205
1206 for (auto & port : mCVPorts)
1207 {
1208 port->mBuffer.reset();
1209 }
1210
1211 mMasterIn.reset();
1212 mMasterOut.reset();
1213
1214 return true;
1215 }
1216
RealtimeAddProcessor(unsigned WXUNUSED (numChannels),float sampleRate)1217 bool LV2Effect::RealtimeAddProcessor(unsigned WXUNUSED(numChannels), float sampleRate)
1218 {
1219 LV2Wrapper *slave = InitInstance(sampleRate);
1220 if (!slave)
1221 {
1222 return false;
1223 }
1224
1225 mSlaves.push_back(slave);
1226
1227 lilv_instance_activate(slave->GetInstance());
1228 mActivated = true;
1229
1230 return true;
1231 }
1232
RealtimeSuspend()1233 bool LV2Effect::RealtimeSuspend()
1234 {
1235 mPositionSpeed = 0.0;
1236 mPositionFrame = 0.0;
1237 mRolling = false;
1238
1239 return true;
1240 }
1241
RealtimeResume()1242 bool LV2Effect::RealtimeResume()
1243 {
1244 mPositionSpeed = 1.0;
1245 mPositionFrame = 0.0;
1246 mRolling = true;
1247
1248 return true;
1249 }
1250
RealtimeProcessStart()1251 bool LV2Effect::RealtimeProcessStart()
1252 {
1253 int i = 0;
1254 for (auto & port : mAudioPorts)
1255 {
1256 if (port->mIsInput)
1257 {
1258 memset(mMasterIn[i++].get(), 0, mBlockSize * sizeof(float));
1259 }
1260 }
1261
1262 mNumSamples = 0;
1263
1264 // Transfer incoming events from the ring buffer to the event buffer for each
1265 // atom input port. These will be made available to each slave in the chain and
1266 // to the master once all slaves have run.
1267 //
1268 // In addition, reset the output Atom ports.
1269 for (auto & port : mAtomPorts)
1270 {
1271 uint8_t *buf = port->mBuffer.data();
1272
1273 if (port->mIsInput)
1274 {
1275 lv2_atom_forge_set_buffer(&mForge,
1276 buf,
1277 port->mBuffer.size());
1278
1279 LV2_Atom_Forge_Frame seqFrame;
1280 LV2_Atom_Sequence *seq = (LV2_Atom_Sequence *)
1281 lv2_atom_forge_sequence_head(&mForge, &seqFrame, 0);
1282
1283 if (port->mWantsPosition)
1284 {
1285 lv2_atom_forge_frame_time(&mForge, mPositionFrame);
1286
1287 LV2_Atom_Forge_Frame posFrame;
1288 lv2_atom_forge_object(&mForge, &posFrame, 0, urid_Position);
1289 lv2_atom_forge_key(&mForge, urid_Speed);
1290 lv2_atom_forge_float(&mForge, mPositionSpeed);
1291 lv2_atom_forge_key(&mForge, urid_Frame);
1292 lv2_atom_forge_long(&mForge, mPositionFrame);
1293 lv2_atom_forge_pop(&mForge, &posFrame);
1294 }
1295
1296 ZixRing *ring = port->mRing;
1297 LV2_Atom atom;
1298 while (zix_ring_read(ring, &atom, sizeof(atom)))
1299 {
1300 if (mForge.offset + sizeof(LV2_Atom_Event) + atom.size < mForge.size)
1301 {
1302 lv2_atom_forge_frame_time(&mForge, mPositionFrame);
1303
1304 lv2_atom_forge_write(&mForge, &atom, sizeof(atom));
1305 zix_ring_read(ring, &mForge.buf[mForge.offset], atom.size);
1306 mForge.offset += atom.size;
1307 seq->atom.size += atom.size;
1308 }
1309 else
1310 {
1311 zix_ring_skip(ring, atom.size);
1312 wxLogError(wxT("LV2 sequence buffer overflow"));
1313 }
1314 }
1315 lv2_atom_forge_pop(&mForge, &seqFrame);
1316 #if 0
1317 LV2_ATOM_SEQUENCE_FOREACH(seq, ev)
1318 {
1319 LV2_Atom_Object *o = (LV2_Atom_Object *) &ev->body;
1320 wxLogDebug(wxT("ev = %lld ev.size %d ev.type %d"), ev->time.frames, ev->body.size, ev->body.type);
1321 }
1322 #endif
1323 }
1324 else
1325 {
1326 port->mBuffer.resize(port->mMinimumSize);
1327 *((LV2_Atom *) buf) =
1328 {
1329 port->mMinimumSize,
1330 urid_Chunk
1331 };
1332 }
1333 }
1334
1335 return true;
1336 }
1337
RealtimeProcess(int group,float ** inbuf,float ** outbuf,size_t numSamples)1338 size_t LV2Effect::RealtimeProcess(int group, float **inbuf, float **outbuf, size_t numSamples)
1339 {
1340 wxASSERT(group >= 0 && group < (int) mSlaves.size());
1341 wxASSERT(numSamples <= (size_t) mBlockSize);
1342
1343 if (group < 0 || group >= (int) mSlaves.size())
1344 {
1345 return 0;
1346 }
1347
1348 LV2Wrapper *slave = mSlaves[group];
1349 LilvInstance *instance = slave->GetInstance();
1350
1351 int i = 0;
1352 int o = 0;
1353 for (auto & port : mAudioPorts)
1354 {
1355 if (port->mIsInput)
1356 {
1357 for (decltype(numSamples) s = 0; s < numSamples; s++)
1358 {
1359 mMasterIn[i][s] += inbuf[i][s];
1360 }
1361 }
1362
1363 lilv_instance_connect_port(instance,
1364 port->mIndex,
1365 (port->mIsInput ? inbuf[i++] : outbuf[o++]));
1366 }
1367
1368 mNumSamples = wxMax(numSamples, mNumSamples);
1369
1370 if (mRolling)
1371 {
1372 lilv_instance_run(instance, numSamples);
1373 }
1374 else
1375 {
1376 while (--i >= 0)
1377 {
1378 for (decltype(numSamples) s = 0; s < numSamples; s++)
1379 {
1380 outbuf[i][s] = inbuf[i][s];
1381 }
1382 }
1383 }
1384
1385 slave->SendResponses();
1386
1387 for (auto & port : mAtomPorts)
1388 {
1389 uint8_t *buf = port->mBuffer.data();
1390
1391 if (!port->mIsInput)
1392 {
1393 port->mBuffer.resize(port->mMinimumSize);
1394
1395 LV2_Atom *chunk = ( LV2_Atom *) buf;
1396 chunk->size = port->mMinimumSize;
1397 chunk->type = urid_Chunk;
1398 }
1399 }
1400
1401 if (group == 0)
1402 {
1403 mPositionFrame += numSamples;
1404 }
1405
1406 return numSamples;
1407 }
1408
RealtimeProcessEnd()1409 bool LV2Effect::RealtimeProcessEnd()
1410 {
1411 // Nothing to do if we did process any samples
1412 if (mNumSamples == 0)
1413 {
1414 return true;
1415 }
1416
1417 int i = 0;
1418 int o = 0;
1419 for (auto & port : mAudioPorts)
1420 {
1421 lilv_instance_connect_port(mMaster->GetInstance(),
1422 port->mIndex,
1423 (port->mIsInput ? mMasterIn[i++].get() : mMasterOut[o++].get()));
1424 }
1425
1426 if (mRolling)
1427 {
1428 lilv_instance_run(mMaster->GetInstance(), mNumSamples);
1429 }
1430
1431 for (auto & port : mAtomPorts)
1432 {
1433 if (!port->mIsInput)
1434 {
1435 ZixRing *ring = port->mRing;
1436
1437 LV2_ATOM_SEQUENCE_FOREACH((LV2_Atom_Sequence *) port->mBuffer.data(), ev)
1438 {
1439 zix_ring_write(ring, &ev->body, ev->body.size + sizeof(LV2_Atom));
1440 }
1441 }
1442 }
1443
1444 mNumSamples = 0;
1445
1446 return true;
1447 }
1448
ShowInterface(wxWindow & parent,const EffectDialogFactory & factory,bool forceModal)1449 bool LV2Effect::ShowInterface(
1450 wxWindow &parent, const EffectDialogFactory &factory, bool forceModal)
1451 {
1452 if (mDialog)
1453 {
1454 if (mDialog->Close(true))
1455 {
1456 mDialog = nullptr;
1457 }
1458 return false;
1459 }
1460
1461 // mDialog is null
1462 auto cleanup = valueRestorer(mDialog);
1463
1464 if ( factory )
1465 mDialog = factory(parent, mHost, this);
1466 if (!mDialog)
1467 {
1468 return false;
1469 }
1470
1471 // Try to give the window a sensible default/minimum size
1472 mDialog->Layout();
1473 mDialog->Fit();
1474 mDialog->SetMinSize(mDialog->GetSize());
1475 if (mNoResize)
1476 {
1477 mDialog->SetMaxSize(mDialog->GetSize());
1478 }
1479
1480 if ((SupportsRealtime() || GetType() == EffectTypeAnalyze) && !forceModal)
1481 {
1482 mDialog->Show();
1483 cleanup.release();
1484
1485 return false;
1486 }
1487
1488 bool res = mDialog->ShowModal() != 0;
1489
1490 return res;
1491 }
1492
GetAutomationParameters(CommandParameters & parms)1493 bool LV2Effect::GetAutomationParameters(CommandParameters &parms)
1494 {
1495 for (auto & port : mControlPorts)
1496 {
1497 if (port->mIsInput)
1498 {
1499 if (!parms.Write(port->mName, port->mVal))
1500 {
1501 return false;
1502 }
1503 }
1504 }
1505
1506 return true;
1507 }
1508
SetAutomationParameters(CommandParameters & parms)1509 bool LV2Effect::SetAutomationParameters(CommandParameters &parms)
1510 {
1511 // First pass validates values
1512 for (auto & port : mControlPorts)
1513 {
1514 if (port->mIsInput)
1515 {
1516 double d = 0.0;
1517 if (!parms.Read(port->mName, &d))
1518 {
1519 return false;
1520 }
1521
1522 // Use unscaled range here
1523 if (d < port->mMin || d > port->mMax)
1524 {
1525 return false;
1526 }
1527 }
1528 }
1529
1530 // Second pass actually sets the values
1531 for (auto & port : mControlPorts)
1532 {
1533 if (port->mIsInput)
1534 {
1535 double d = 0.0;
1536 if (!parms.Read(port->mName, &d))
1537 {
1538 return false;
1539 }
1540
1541 port->mVal = d;
1542 port->mTmp = port->mVal * (port->mSampleRate ? mSampleRate : 1.0);
1543 }
1544 }
1545
1546 return true;
1547 }
1548
1549 // ============================================================================
1550 // EffectUIClientInterface Implementation
1551 // ============================================================================
1552
SetHostUI(EffectUIHostInterface * host)1553 void LV2Effect::SetHostUI(EffectUIHostInterface *host)
1554 {
1555 mUIHost = host;
1556 }
1557
PopulateUI(ShuttleGui & S)1558 bool LV2Effect::PopulateUI(ShuttleGui &S)
1559 {
1560 auto parent = S.GetParent();
1561 mParent = parent;
1562
1563 mParent->PushEventHandler(this);
1564
1565 mSuilHost = NULL;
1566 mSuilInstance = NULL;
1567
1568 mMaster = InitInstance(mSampleRate);
1569 if (mMaster == NULL)
1570 {
1571 AudacityMessageBox( XO("Couldn't instantiate effect") );
1572 return false;
1573 }
1574
1575 // Determine if the GUI editor is supposed to be used or not
1576 mHost->GetSharedConfig(wxT("Settings"),
1577 wxT("UseGUI"),
1578 mUseGUI,
1579 true);
1580
1581 // Until I figure out where to put the "Duration" control in the
1582 // graphical editor, force usage of plain editor.
1583 if (GetType() == EffectTypeGenerate)
1584 {
1585 mUseGUI = false;
1586 }
1587
1588 if (mUseGUI)
1589 {
1590 mUseGUI = BuildFancy();
1591 }
1592
1593 if (!mUseGUI)
1594 {
1595 return BuildPlain();
1596 }
1597
1598 return true;
1599 }
1600
IsGraphicalUI()1601 bool LV2Effect::IsGraphicalUI()
1602 {
1603 return mUseGUI;
1604 }
1605
ValidateUI()1606 bool LV2Effect::ValidateUI()
1607 {
1608 if (!mParent->Validate() || !mParent->TransferDataFromWindow())
1609 {
1610 return false;
1611 }
1612
1613 if (GetType() == EffectTypeGenerate)
1614 {
1615 mHost->SetDuration(mDuration->GetValue());
1616 }
1617
1618 return true;
1619 }
1620
HideUI()1621 bool LV2Effect::HideUI()
1622 {
1623 #if 0
1624 // Nothing to do yet
1625 #endif
1626 return true;
1627 }
1628
CloseUI()1629 bool LV2Effect::CloseUI()
1630 {
1631 #ifdef __WXMAC__
1632 #ifdef __WX_EVTLOOP_BUSY_WAITING__
1633 wxEventLoop::SetBusyWaiting(false);
1634 #endif
1635 #endif
1636
1637 mParent->RemoveEventHandler(this);
1638
1639 if (mSuilInstance)
1640 {
1641 if (mNativeWin)
1642 {
1643 mNativeWin->Destroy();
1644 mNativeWin = NULL;
1645 }
1646
1647 mUIIdleInterface = NULL;
1648 mUIShowInterface = NULL;
1649 mExternalWidget = NULL;
1650
1651 suil_instance_free(mSuilInstance);
1652 mSuilInstance = NULL;
1653 }
1654
1655 if (mSuilHost)
1656 {
1657 suil_host_free(mSuilHost);
1658 mSuilHost = NULL;
1659 }
1660
1661 if (mMaster)
1662 {
1663 FreeInstance(mMaster);
1664 mMaster = NULL;
1665 }
1666
1667 mUIHost = NULL;
1668 mParent = NULL;
1669 mDialog = NULL;
1670
1671 return true;
1672 }
1673
LoadUserPreset(const RegistryPath & name)1674 bool LV2Effect::LoadUserPreset(const RegistryPath &name)
1675 {
1676 if (!LoadParameters(name))
1677 {
1678 return false;
1679 }
1680
1681 return TransferDataToWindow();
1682 }
1683
SaveUserPreset(const RegistryPath & name)1684 bool LV2Effect::SaveUserPreset(const RegistryPath &name)
1685 {
1686 return SaveParameters(name);
1687 }
1688
GetFactoryPresets()1689 RegistryPaths LV2Effect::GetFactoryPresets()
1690 {
1691 if (mFactoryPresetsLoaded)
1692 {
1693 return mFactoryPresetNames;
1694 }
1695
1696 LilvNodes *presets = lilv_plugin_get_related(mPlug, node_Preset);
1697 if (presets)
1698 {
1699 LILV_FOREACH(nodes, i, presets)
1700 {
1701 const LilvNode *preset = lilv_nodes_get(presets, i);
1702
1703 mFactoryPresetUris.push_back(LilvString(preset));
1704
1705 lilv_world_load_resource(gWorld, preset);
1706
1707 LilvNodes *labels = lilv_world_find_nodes(gWorld, preset, node_Label, NULL);
1708 if (labels)
1709 {
1710 const LilvNode *label = lilv_nodes_get_first(labels);
1711
1712 mFactoryPresetNames.push_back(LilvString(label));
1713
1714 lilv_nodes_free(labels);
1715 }
1716 else
1717 {
1718 mFactoryPresetNames.push_back(LilvString(preset).AfterLast(wxT('#')));
1719 }
1720 }
1721
1722 lilv_nodes_free(presets);
1723 }
1724
1725 mFactoryPresetsLoaded = true;
1726
1727 return mFactoryPresetNames;
1728 }
1729
LoadFactoryPreset(int id)1730 bool LV2Effect::LoadFactoryPreset(int id)
1731 {
1732 if (id < 0 || id >= (int) mFactoryPresetUris.size())
1733 {
1734 return false;
1735 }
1736
1737 LilvNode *preset = lilv_new_uri(gWorld, mFactoryPresetUris[id].ToUTF8());
1738 if (!preset)
1739 {
1740 return false;
1741 }
1742
1743 LilvState *state = lilv_state_new_from_world(gWorld, &mURIDMapFeature, preset);
1744 if (state)
1745 {
1746 lilv_state_restore(state, mMaster->GetInstance(), set_value_func, this, 0, NULL);
1747
1748 lilv_state_free(state);
1749
1750 TransferDataToWindow();
1751 }
1752
1753 lilv_node_free(preset);
1754
1755 return state != NULL;
1756 }
1757
LoadFactoryDefaults()1758 bool LV2Effect::LoadFactoryDefaults()
1759 {
1760 if (!LoadParameters(mHost->GetFactoryDefaultsGroup()))
1761 {
1762 return false;
1763 }
1764
1765 return TransferDataToWindow();
1766 }
1767
CanExportPresets()1768 bool LV2Effect::CanExportPresets()
1769 {
1770 return false;
1771 }
1772
ExportPresets()1773 void LV2Effect::ExportPresets()
1774 {
1775 }
1776
ImportPresets()1777 void LV2Effect::ImportPresets()
1778 {
1779 }
1780
HasOptions()1781 bool LV2Effect::HasOptions()
1782 {
1783 return true;
1784 }
1785
ShowOptions()1786 void LV2Effect::ShowOptions()
1787 {
1788 LV2EffectSettingsDialog dlg(mParent, this);
1789 if (dlg.ShowModal() == wxID_OK)
1790 {
1791 // Reinitialize configuration settings
1792 int userBlockSize;
1793 mHost->GetSharedConfig(wxT("Settings"), wxT("BufferSize"), userBlockSize, DEFAULT_BLOCKSIZE);
1794 mUserBlockSize = std::max(1, userBlockSize);
1795 mHost->GetSharedConfig(wxT("Settings"), wxT("UseLatency"), mUseLatency, true);
1796 }
1797 }
1798
1799 // ============================================================================
1800 // LV2Effect Implementation
1801 // ============================================================================
1802
LoadParameters(const RegistryPath & group)1803 bool LV2Effect::LoadParameters(const RegistryPath &group)
1804 {
1805 wxString parms;
1806 if (!mHost->GetPrivateConfig(group, wxT("Parameters"), parms, wxEmptyString))
1807 {
1808 return false;
1809 }
1810
1811 CommandParameters eap;
1812 if (!eap.SetParameters(parms))
1813 {
1814 return false;
1815 }
1816
1817 return SetAutomationParameters(eap);
1818 }
1819
SaveParameters(const RegistryPath & group)1820 bool LV2Effect::SaveParameters(const RegistryPath &group)
1821 {
1822 CommandParameters eap;
1823 if (!GetAutomationParameters(eap))
1824 {
1825 return false;
1826 }
1827
1828 wxString parms;
1829 if (!eap.GetParameters(parms))
1830 {
1831 return false;
1832 }
1833
1834 return mHost->SetPrivateConfig(group, wxT("Parameters"), parms);
1835 }
1836
AddOption(LV2_URID key,uint32_t size,LV2_URID type,const void * value)1837 size_t LV2Effect::AddOption(LV2_URID key, uint32_t size, LV2_URID type, const void *value)
1838 {
1839 int ndx = mOptions.size();
1840 mOptions.resize(1 + ndx);
1841
1842 memset(&mOptions[ndx], 0, sizeof(mOptions[ndx]));
1843
1844 if (key != 0)
1845 {
1846 mOptions[ndx].context = LV2_OPTIONS_INSTANCE;
1847 mOptions[ndx].subject = 0;
1848 mOptions[ndx].key = key;
1849 mOptions[ndx].size = size;
1850 mOptions[ndx].type = type;
1851 mOptions[ndx].value = value;
1852 }
1853
1854 return ndx;
1855 }
1856
AddFeature(const char * uri,void * data)1857 LV2_Feature *LV2Effect::AddFeature(const char *uri, void *data)
1858 {
1859 size_t ndx = mFeatures.size();
1860 mFeatures.resize(1 + ndx);
1861
1862 if (uri)
1863 {
1864 mFeatures[ndx].reset(safenew LV2_Feature);
1865 mFeatures[ndx]->URI = uri;
1866 mFeatures[ndx]->data = data;
1867 }
1868
1869 return mFeatures[ndx].get();
1870 }
1871
ValidateFeatures(const LilvNode * subject)1872 bool LV2Effect::ValidateFeatures(const LilvNode *subject)
1873 {
1874 if (CheckFeatures(subject, node_RequiredFeature, true))
1875 {
1876 return CheckFeatures(subject, node_OptionalFeature, false);
1877 }
1878
1879 return false;
1880 }
1881
CheckFeatures(const LilvNode * subject,const LilvNode * predicate,bool required)1882 bool LV2Effect::CheckFeatures(const LilvNode *subject, const LilvNode *predicate, bool required)
1883 {
1884 bool supported = true;
1885
1886 LilvNodes *nodes = lilv_world_find_nodes(gWorld, subject, predicate, NULL);
1887 if (nodes)
1888 {
1889 LILV_FOREACH(nodes, i, nodes)
1890 {
1891 const LilvNode *node = lilv_nodes_get(nodes, i);
1892 const char *uri = lilv_node_as_string(node);
1893
1894 if ((strcmp(uri, LV2_UI__noUserResize) == 0) ||
1895 (strcmp(uri, LV2_UI__fixedSize) == 0))
1896 {
1897 mNoResize = true;
1898 }
1899 else if (strcmp(uri, LV2_WORKER__schedule) == 0)
1900 {
1901 /* Supported but handled in LV2Wrapper */
1902 }
1903 else
1904 {
1905 supported = false;
1906
1907 for (auto & feature : mFeatures)
1908 {
1909 if (feature && strcmp(feature->URI, uri) == 0)
1910 {
1911 supported = true;
1912 break;
1913 }
1914 }
1915
1916 if (!supported)
1917 {
1918 if (required)
1919 {
1920 wxLogError(wxT("%s requires unsupported feature %s"), lilv_node_as_string(lilv_plugin_get_uri(mPlug)), uri);
1921 printf(_("%s requires unsupported feature %s\n"), lilv_node_as_string(lilv_plugin_get_uri(mPlug)), uri);
1922 break;
1923 }
1924 supported = true;
1925 }
1926 }
1927 }
1928
1929 lilv_nodes_free(nodes);
1930 }
1931
1932
1933 return supported;
1934 }
1935
ValidateOptions(const LilvNode * subject)1936 bool LV2Effect::ValidateOptions(const LilvNode *subject)
1937 {
1938 if (CheckOptions(subject, node_RequiredOption, true))
1939 {
1940 return CheckOptions(subject, node_SupportedOption, false);
1941 }
1942
1943 return false;
1944 }
1945
CheckOptions(const LilvNode * subject,const LilvNode * predicate,bool required)1946 bool LV2Effect::CheckOptions(const LilvNode *subject, const LilvNode *predicate, bool required)
1947 {
1948 bool supported = true;
1949
1950 LilvNodes *nodes = lilv_world_find_nodes(gWorld, subject, predicate, NULL);
1951 if (nodes)
1952 {
1953 LILV_FOREACH(nodes, i, nodes)
1954 {
1955 const LilvNode *node = lilv_nodes_get(nodes, i);
1956 const char *uri = lilv_node_as_string(node);
1957 LV2_URID urid = URID_Map(uri);
1958
1959 if (urid == urid_NominalBlockLength)
1960 {
1961 mSupportsNominalBlockLength = true;
1962 }
1963 else if (urid == urid_SampleRate)
1964 {
1965 mSupportsSampleRate = true;
1966 }
1967 else
1968 {
1969 supported = false;
1970
1971 for (auto & option : mOptions)
1972 {
1973 if (option.key == urid)
1974 {
1975 supported = true;
1976 break;
1977 }
1978 }
1979
1980 if (!supported)
1981 {
1982 if (required)
1983 {
1984 wxLogError(wxT("%s requires unsupported option %s"), lilv_node_as_string(lilv_plugin_get_uri(mPlug)), uri);
1985 printf(_("%s requires unsupported option %s\n"), lilv_node_as_string(lilv_plugin_get_uri(mPlug)), uri);
1986 break;
1987 }
1988 supported = true;
1989 }
1990 }
1991 }
1992
1993 lilv_nodes_free(nodes);
1994 }
1995
1996 return supported;
1997 }
1998
InitInstance(float sampleRate)1999 LV2Wrapper *LV2Effect::InitInstance(float sampleRate)
2000 {
2001 LV2Wrapper *wrapper = new LV2Wrapper(this);
2002 if (wrapper == NULL)
2003 {
2004 return NULL;
2005 }
2006
2007 LilvInstance *instance = wrapper->Instantiate(mPlug, sampleRate, mFeatures);
2008 if (!instance)
2009 {
2010 delete wrapper;
2011 return NULL;
2012 }
2013
2014 wrapper->SetBlockSize();
2015 wrapper->SetSampleRate();
2016
2017 // Connect all control ports
2018 for (auto & port : mControlPorts)
2019 {
2020 // If it's not an input port and master has already been created
2021 // then connect the port to a dummy field since slave output port
2022 // values are unwanted as the master values will be used.
2023 //
2024 // Otherwise, connect it to the real value field.
2025 lilv_instance_connect_port(instance,
2026 port->mIndex,
2027 !port->mIsInput && mMaster
2028 ? &port->mDmy
2029 : &port->mVal);
2030 }
2031
2032 // Connect all atom ports
2033 for (auto & port : mAtomPorts)
2034 {
2035 lilv_instance_connect_port(instance, port->mIndex, port->mBuffer.data());
2036 }
2037
2038 // We don't fully support CV ports, so connect them to dummy buffers for now.
2039 for (auto & port : mCVPorts)
2040 {
2041 lilv_instance_connect_port(instance, port->mIndex, port->mBuffer.get());
2042 }
2043
2044 // Give plugin a chance to initialize. The SWH plugins (like AllPass) need
2045 // this before it can be safely deleted.
2046 lilv_instance_activate(instance);
2047 lilv_instance_deactivate(instance);
2048
2049 for (auto & port : mAtomPorts)
2050 {
2051 if (!port->mIsInput)
2052 {
2053 ZixRing *ring = port->mRing;
2054
2055 LV2_ATOM_SEQUENCE_FOREACH(( LV2_Atom_Sequence *) port->mBuffer.data(), ev)
2056 {
2057 zix_ring_write(ring, &ev->body, ev->body.size + sizeof(LV2_Atom));
2058 }
2059 }
2060 }
2061
2062 return wrapper;
2063 }
2064
FreeInstance(LV2Wrapper * wrapper)2065 void LV2Effect::FreeInstance(LV2Wrapper *wrapper)
2066 {
2067 delete wrapper;
2068 }
2069
BuildFancy()2070 bool LV2Effect::BuildFancy()
2071 {
2072 // Set the native UI type
2073 const char *nativeType =
2074 #if defined(__WXGTK3__)
2075 LV2_UI__Gtk3UI;
2076 #elif defined(__WXGTK__)
2077 LV2_UI__GtkUI;
2078 #elif defined(__WXMSW__)
2079 LV2_UI__WindowsUI;
2080 #elif defined(__WXMAC__)
2081 LV2_UI__CocoaUI;
2082 #endif
2083
2084 // Determine if the plugin has a supported UI
2085 const LilvUI *ui = NULL;
2086 const LilvNode *uiType = NULL;
2087 LilvUIs *uis = lilv_plugin_get_uis(mPlug);
2088 if (uis)
2089 {
2090 LilvNode *containerType = lilv_new_uri(gWorld, nativeType);
2091 if (containerType)
2092 {
2093 LILV_FOREACH(uis, iter, uis)
2094 {
2095 ui = lilv_uis_get(uis, iter);
2096 if (lilv_ui_is_supported(ui, suil_ui_supported, containerType, &uiType))
2097 {
2098 break;
2099 }
2100 if (lilv_ui_is_a(ui, node_Gtk) || lilv_ui_is_a(ui, node_Gtk3))
2101 {
2102 uiType = node_Gtk;
2103 break;
2104 }
2105
2106 ui = NULL;
2107 }
2108
2109 lilv_node_free(containerType);
2110 }
2111 }
2112
2113 // Check for other supported UIs
2114 if (ui == NULL)
2115 {
2116 LILV_FOREACH(uis, iter, uis)
2117 {
2118 ui = lilv_uis_get(uis, iter);
2119 if (lilv_ui_is_a(ui, node_ExternalUI) || lilv_ui_is_a(ui, node_ExternalUIOld))
2120 {
2121 uiType = node_ExternalUI;
2122 break;
2123 }
2124 ui = NULL;
2125 }
2126 }
2127
2128 // No usable UI found
2129 if (ui == NULL)
2130 {
2131 lilv_uis_free(uis);
2132 return false;
2133 }
2134
2135 const LilvNode *uinode = lilv_ui_get_uri(ui);
2136 lilv_world_load_resource(gWorld, uinode);
2137 if (!ValidateFeatures(uinode))
2138 {
2139 lilv_uis_free(uis);
2140 return false;
2141 }
2142
2143 const char *containerType;
2144
2145 if (uiType == node_ExternalUI)
2146 {
2147 containerType = LV2_EXTERNAL_UI__Widget;
2148 }
2149 else
2150 {
2151 containerType = nativeType;
2152 mParentFeature->data = mParent->GetHandle();
2153
2154 #if defined(__WXGTK__)
2155 // Make sure the parent has a window
2156 if (!gtk_widget_get_window(GTK_WIDGET(mParent->m_wxwindow)))
2157 {
2158 gtk_widget_realize(GTK_WIDGET(mParent->m_wxwindow));
2159 }
2160 #endif
2161 }
2162
2163 LilvInstance *instance = mMaster->GetInstance();
2164 mInstanceAccessFeature->data = lilv_instance_get_handle(instance);
2165 mExtensionDataFeature.data_access = lilv_instance_get_descriptor(instance)->extension_data;
2166
2167 // Set before creating the UI instance so the initial size (if any) can be captured
2168 mNativeWinInitialSize = wxDefaultSize;
2169 mNativeWinLastSize = wxDefaultSize;
2170
2171 // Create the suil host
2172 mSuilHost = suil_host_new(LV2Effect::suil_port_write_func,
2173 LV2Effect::suil_port_index_func,
2174 NULL,
2175 NULL);
2176 if (!mSuilHost)
2177 {
2178 lilv_uis_free(uis);
2179 return false;
2180 }
2181
2182 #if defined(__WXMSW__)
2183 // Plugins may have dependencies that need to be loaded from the same path
2184 // as the main DLL, so add this plugin's path to the DLL search order.
2185 char *libPath = lilv_file_uri_parse(lilv_node_as_uri(lilv_ui_get_binary_uri(ui)), NULL);
2186 wxString path = wxPathOnly(libPath);
2187 SetDllDirectory(path.c_str());
2188 lilv_free(libPath);
2189 #endif
2190
2191 char *bundlePath = lilv_file_uri_parse(lilv_node_as_uri(lilv_ui_get_bundle_uri(ui)), NULL);
2192 char *binaryPath = lilv_file_uri_parse(lilv_node_as_uri(lilv_ui_get_binary_uri(ui)), NULL);
2193
2194 mSuilInstance = suil_instance_new(mSuilHost,
2195 this,
2196 containerType,
2197 lilv_node_as_uri(lilv_plugin_get_uri(mPlug)),
2198 lilv_node_as_uri(lilv_ui_get_uri(ui)),
2199 lilv_node_as_uri(uiType),
2200 bundlePath,
2201 binaryPath,
2202 reinterpret_cast<const LV2_Feature * const *>(mFeatures.data()));
2203
2204 lilv_free(binaryPath);
2205 lilv_free(bundlePath);
2206 lilv_uis_free(uis);
2207
2208 // Bail if the instance (no compatible UI) couldn't be created
2209 if (!mSuilInstance)
2210 {
2211 #if defined(__WXMSW__)
2212 SetDllDirectory(NULL);
2213 #endif
2214
2215 suil_host_free(mSuilHost);
2216 mSuilHost = NULL;
2217
2218 return false;
2219 }
2220
2221 if (uiType == node_ExternalUI)
2222 {
2223 mParent->SetMinSize(wxDefaultSize);
2224
2225 mExternalWidget = (LV2_External_UI_Widget *) suil_instance_get_widget(mSuilInstance);
2226 mTimer.SetOwner(this, ID_TIMER);
2227 mTimer.Start(20);
2228
2229 LV2_EXTERNAL_UI_SHOW(mExternalWidget);
2230 }
2231 else
2232 {
2233 WXWidget widget = (WXWidget) suil_instance_get_widget(mSuilInstance);
2234
2235 #if defined(__WXGTK__)
2236 // Needed by some plugins (e.g., Invada) to ensure the display is fully
2237 // populated.
2238 gtk_widget_show_all(widget);
2239
2240 // See note at size_request()
2241 g_signal_connect(widget, "size-request", G_CALLBACK(LV2Effect::size_request), this);
2242 #endif
2243
2244 Destroy_ptr< NativeWindow > uNativeWin{ safenew NativeWindow() };
2245 if ( !uNativeWin->Create(mParent, widget) )
2246 return false;
2247 mNativeWin = uNativeWin.release();
2248
2249 mNativeWin->Bind(wxEVT_SIZE, &LV2Effect::OnSize, this);
2250
2251 // The plugin called the LV2UI_Resize::ui_resize function to set the size before
2252 // the native window was created, so set the size now.
2253 if (mNativeWinInitialSize != wxDefaultSize)
2254 {
2255 mNativeWin->SetMinSize(mNativeWinInitialSize);
2256 }
2257
2258 wxSizerItem *si = NULL;
2259 auto vs = std::make_unique<wxBoxSizer>(wxVERTICAL);
2260 if (vs)
2261 {
2262 auto hs = std::make_unique<wxBoxSizer>(wxHORIZONTAL);
2263 if (hs)
2264 {
2265 if (mNoResize)
2266 {
2267 si = hs->Add(mNativeWin, 0, wxCENTER);
2268 vs->Add(hs.release(), 1, wxCENTER);
2269 }
2270 else
2271 {
2272 si = hs->Add(mNativeWin, 1, wxEXPAND);
2273 vs->Add(hs.release(), 1, wxEXPAND);
2274 }
2275 }
2276 }
2277
2278 if (!si)
2279 {
2280 lilv_uis_free(uis);
2281 return false;
2282 }
2283
2284 mParent->SetSizerAndFit(vs.release());
2285 }
2286
2287 mUIIdleInterface = (LV2UI_Idle_Interface *)
2288 suil_instance_extension_data(mSuilInstance, LV2_UI__idleInterface);
2289
2290 mUIShowInterface = (LV2UI_Show_Interface *)
2291 suil_instance_extension_data(mSuilInstance, LV2_UI__showInterface);
2292
2293 if (mUIShowInterface)
2294 {
2295 // mUIShowInterface->show(suil_instance_get_handle(mSuilInstance));
2296 }
2297
2298 TransferDataToWindow();
2299
2300 #ifdef __WXMAC__
2301 #ifdef __WX_EVTLOOP_BUSY_WAITING__
2302 wxEventLoop::SetBusyWaiting(true);
2303 #endif
2304 #endif
2305
2306 #if defined(__WXMSW__)
2307 SetDllDirectory(NULL);
2308 #endif
2309
2310 return true;
2311 }
2312
BuildPlain()2313 bool LV2Effect::BuildPlain()
2314 {
2315 int numCols = 5;
2316 wxSizer *innerSizer;
2317
2318 wxASSERT(mParent); // To justify safenew
2319 wxScrolledWindow *const w = safenew
2320 wxScrolledWindow(mParent,
2321 wxID_ANY,
2322 wxDefaultPosition,
2323 wxDefaultSize,
2324 wxVSCROLL | wxTAB_TRAVERSAL);
2325
2326 {
2327 auto outerSizer = std::make_unique<wxBoxSizer>(wxVERTICAL);
2328 w->SetScrollRate(0, 20);
2329
2330 // This fools NVDA into not saying "Panel" when the dialog gets focus
2331 w->SetName(wxT("\a"));
2332 w->SetLabel(wxT("\a"));
2333
2334 outerSizer->Add(w, 1, wxEXPAND);
2335
2336 {
2337 auto uInnerSizer = std::make_unique<wxBoxSizer>(wxVERTICAL);
2338 innerSizer = uInnerSizer.get();
2339
2340 if (GetType() == EffectTypeGenerate)
2341 {
2342 // Add the length control
2343 auto groupSizer = std::make_unique<wxStaticBoxSizer>(wxVERTICAL, w, _("Generator"));
2344
2345 auto sizer = std::make_unique<wxBoxSizer>(wxHORIZONTAL);
2346
2347 wxWindow *item = safenew wxStaticText(w, 0, _("&Duration:"));
2348 sizer->Add(item, 0, wxALIGN_CENTER | wxALL, 5);
2349 mDuration = safenew
2350 NumericTextCtrl(w, ID_Duration,
2351 NumericConverter::TIME,
2352 mHost->GetDurationFormat(),
2353 mHost->GetDuration(),
2354 mSampleRate,
2355 NumericTextCtrl::Options {}
2356 .AutoPos(true));
2357 mDuration->SetName( XO("Duration") );
2358 sizer->Add(mDuration, 0, wxALIGN_CENTER | wxALL, 5);
2359
2360 groupSizer->Add(sizer.release(), 0, wxALIGN_CENTER | wxALL, 5);
2361 innerSizer->Add(groupSizer.release(), 0, wxEXPAND | wxALL, 5);
2362 }
2363
2364 std::sort(mGroups.begin(), mGroups.end());
2365
2366 for (size_t i = 0, groupCount = mGroups.size(); i < groupCount; i++)
2367 {
2368 wxString label = mGroups[i];
2369 auto groupSizer = std::make_unique<wxStaticBoxSizer>(wxVERTICAL, w, label);
2370
2371 auto gridSizer = std::make_unique<wxFlexGridSizer>(numCols, 5, 5);
2372 gridSizer->AddGrowableCol(3);
2373
2374 for (auto & p : mGroupMap[mGroups[i]])
2375 {
2376 auto & port = mControlPorts[p];
2377
2378 if (port->mNotOnGui)
2379 {
2380 continue;
2381 }
2382
2383 wxString labelText = port->mName;
2384 if (!port->mUnits.empty())
2385 {
2386 labelText += wxT(" (") + port->mUnits + wxT(")");
2387 }
2388
2389 if (port->mTrigger)
2390 {
2391 gridSizer->Add(1, 1, 0);
2392
2393 wxASSERT(w); // To justify safenew
2394 wxButton *b = safenew wxButton(w, ID_Triggers + p, labelText);
2395 gridSizer->Add(b, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_LEFT);
2396 port->mCtrl.button = b;
2397
2398 gridSizer->Add(1, 1, 0);
2399 gridSizer->Add(1, 1, 0);
2400 gridSizer->Add(1, 1, 0);
2401 continue;
2402 }
2403
2404 wxWindow *item = safenew wxStaticText(w, wxID_ANY, labelText + wxT(":"),
2405 wxDefaultPosition, wxDefaultSize,
2406 wxALIGN_RIGHT);
2407 gridSizer->Add(item, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT);
2408
2409 if (port->mToggle)
2410 {
2411 wxCheckBox *c = safenew wxCheckBox(w, ID_Toggles + p, wxT(""));
2412 c->SetName(labelText);
2413 c->SetValue(port->mVal > 0);
2414 gridSizer->Add(c, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_LEFT);
2415 port->mCtrl.checkbox = c;
2416
2417 gridSizer->Add(1, 1, 0);
2418 gridSizer->Add(1, 1, 0);
2419 gridSizer->Add(1, 1, 0);
2420 }
2421 else if (port->mEnumeration) // Check before integer
2422 {
2423 int s;
2424 for (s = (int) port->mScaleValues.size() - 1; s >= 0; s--)
2425 {
2426 if (port->mVal >= port->mScaleValues[s])
2427 {
2428 break;
2429 }
2430 }
2431
2432 if (s < 0)
2433 {
2434 s = 0;
2435 }
2436
2437 wxChoice *c = safenew wxChoice(w, ID_Choices + p);
2438 c->SetName(labelText);
2439 c->Append(port->mScaleLabels);
2440 c->SetSelection(s);
2441 gridSizer->Add(c, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_LEFT);
2442 port->mCtrl.choice = c;
2443
2444 gridSizer->Add(1, 1, 0);
2445 gridSizer->Add(1, 1, 0);
2446 gridSizer->Add(1, 1, 0);
2447 }
2448 else if (!port->mIsInput)
2449 {
2450 gridSizer->Add(1, 1, 0);
2451 gridSizer->Add(1, 1, 0);
2452
2453 LV2EffectMeter *m = safenew LV2EffectMeter(w, port);
2454 gridSizer->Add(m, 0, wxALIGN_CENTER_VERTICAL | wxEXPAND);
2455 port->mCtrl.meter = m;
2456
2457 gridSizer->Add(1, 1, 0);
2458 }
2459 else
2460 {
2461 wxTextCtrl *t = safenew wxTextCtrl(w, ID_Texts + p, wxT(""));
2462 t->SetName(labelText);
2463 gridSizer->Add(t, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_LEFT);
2464 port->mText = t;
2465
2466 float rate = port->mSampleRate ? mSampleRate : 1.0;
2467
2468 port->mLo = port->mMin * rate;
2469 port->mHi = port->mMax * rate;
2470 port->mTmp = port->mVal * rate;
2471
2472 if (port->mInteger)
2473 {
2474 IntegerValidator<float> vld(&port->mTmp);
2475 vld.SetRange(port->mLo, port->mHi);
2476 t->SetValidator(vld);
2477 }
2478 else
2479 {
2480 FloatingPointValidator<float> vld(6, &port->mTmp);
2481 vld.SetRange(port->mLo, port->mHi);
2482
2483 // Set number of decimal places
2484 float range = port->mHi - port->mLo;
2485 auto style = range < 10
2486 ? NumValidatorStyle::THREE_TRAILING_ZEROES
2487 : range < 100
2488 ? NumValidatorStyle::TWO_TRAILING_ZEROES
2489 : NumValidatorStyle::ONE_TRAILING_ZERO;
2490 vld.SetStyle(style);
2491
2492 t->SetValidator(vld);
2493 }
2494
2495 if (port->mHasLo)
2496 {
2497 wxString str;
2498 if (port->mInteger || port->mSampleRate)
2499 {
2500 str.Printf(wxT("%d"), (int) lrintf(port->mLo));
2501 }
2502 else
2503 {
2504 str = Internat::ToDisplayString(port->mLo);
2505 }
2506 item = safenew wxStaticText(w, wxID_ANY, str);
2507 gridSizer->Add(item, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT);
2508 }
2509 else
2510 {
2511 gridSizer->Add(1, 1, 0);
2512 }
2513
2514 wxSlider *s = safenew wxSliderWrapper(w, ID_Sliders + p,
2515 0, 0, 1000,
2516 wxDefaultPosition,
2517 wxSize(150, -1));
2518 s->SetName(labelText);
2519 gridSizer->Add(s, 0, wxALIGN_CENTER_VERTICAL | wxEXPAND);
2520 port->mCtrl.slider = s;
2521
2522 if (port->mHasHi)
2523 {
2524 wxString str;
2525 if (port->mInteger || port->mSampleRate)
2526 {
2527 str.Printf(wxT("%d"), (int) lrintf(port->mHi));
2528 }
2529 else
2530 {
2531 str = Internat::ToDisplayString(port->mHi);
2532 }
2533 item = safenew wxStaticText(w, wxID_ANY, str);
2534 gridSizer->Add(item, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_LEFT);
2535 }
2536 else
2537 {
2538 gridSizer->Add(1, 1, 0);
2539 }
2540 }
2541 }
2542
2543 groupSizer->Add(gridSizer.release(), 1, wxEXPAND | wxALL, 5);
2544 innerSizer->Add(groupSizer.release(), 0, wxEXPAND | wxALL, 5);
2545 }
2546
2547 innerSizer->Layout();
2548
2549 // Calculate the maximum width of all columns (bypass Generator sizer)
2550 std::vector<int> widths(numCols);
2551
2552 size_t cnt = innerSizer->GetChildren().GetCount();
2553 for (size_t i = (GetType() == EffectTypeGenerate); i < cnt; i++)
2554 {
2555 wxSizer *groupSizer = innerSizer->GetItem(i)->GetSizer();
2556 wxFlexGridSizer *gridSizer = (wxFlexGridSizer *) groupSizer->GetItem((size_t) 0)->GetSizer();
2557
2558 size_t items = gridSizer->GetChildren().GetCount();
2559 int cols = gridSizer->GetCols();
2560
2561 for (size_t j = 0; j < items; j++)
2562 {
2563 wxSizerItem *item = gridSizer->GetItem(j);
2564 widths[j % cols] = wxMax(widths[j % cols], item->GetSize().GetWidth());
2565 }
2566 }
2567
2568 // Set each column in all of the groups to the same width.
2569 for (size_t i = (GetType() == EffectTypeGenerate); i < cnt; i++)
2570 {
2571 wxSizer *groupSizer = innerSizer->GetItem(i)->GetSizer();
2572 wxFlexGridSizer *gridSizer = (wxFlexGridSizer *) groupSizer->GetItem((size_t) 0)->GetSizer();
2573
2574 size_t items = gridSizer->GetChildren().GetCount();
2575 int cols = gridSizer->GetCols();
2576
2577 for (size_t j = 0; j < items; j++)
2578 {
2579 wxSizerItem *item = gridSizer->GetItem(j);
2580
2581 int flags = item->GetFlag();
2582 if (flags & wxEXPAND)
2583 {
2584 continue;
2585 }
2586
2587 if (flags & wxALIGN_RIGHT)
2588 {
2589 flags = (flags & ~wxALL) | wxLEFT;
2590 }
2591 else
2592 {
2593 flags = (flags & ~wxALL) | wxRIGHT;
2594 }
2595 item->SetFlag(flags);
2596
2597 item->SetBorder(widths[j % cols] - item->GetMinSize().GetWidth());
2598 }
2599 }
2600
2601 w->SetSizer(uInnerSizer.release());
2602 }
2603
2604 mParent->SetSizer(outerSizer.release());
2605 }
2606
2607 // Try to give the window a sensible default/minimum size
2608 wxSize sz1 = innerSizer->GetMinSize();
2609 wxSize sz2 = mParent->GetMinSize();
2610 w->SetMinSize( { -1, std::min(sz1.y, sz2.y) } );
2611
2612 // And let the parent reduce to the NEW minimum if possible
2613 mParent->SetMinSize(w->GetMinSize());
2614
2615 TransferDataToWindow();
2616
2617 return true;
2618 }
2619
TransferDataToWindow()2620 bool LV2Effect::TransferDataToWindow()
2621 {
2622 if (mUseGUI)
2623 {
2624 if (mSuilInstance)
2625 {
2626 for (auto & port : mControlPorts)
2627 {
2628 if (port->mIsInput)
2629 {
2630 suil_instance_port_event(mSuilInstance,
2631 port->mIndex,
2632 sizeof(float),
2633 0,
2634 &port->mVal);
2635 }
2636 }
2637 }
2638
2639 return true;
2640 }
2641
2642 for (auto & group : mGroups)
2643 {
2644 const auto & params = mGroupMap[group];
2645 for (auto & param : params)
2646 {
2647 auto & port = mControlPorts[param];
2648
2649 if (port->mTrigger)
2650 {
2651 continue;
2652 }
2653
2654 if (port->mToggle)
2655 {
2656 port->mCtrl.checkbox->SetValue(port->mVal > 0);
2657 }
2658 else if (port->mEnumeration) // Check before integer
2659 {
2660 int s;
2661 for (s = (int) port->mScaleValues.size() - 1; s >= 0; s--)
2662 {
2663 if (port->mVal >= port->mScaleValues[s])
2664 {
2665 break;
2666 }
2667 }
2668
2669 if (s < 0)
2670 {
2671 s = 0;
2672 }
2673
2674 port->mCtrl.choice->SetSelection(s);
2675 }
2676 else if (port->mIsInput)
2677 {
2678 port->mTmp = port->mVal * (port->mSampleRate ? mSampleRate : 1.0);
2679 SetSlider(port);
2680 }
2681 }
2682 }
2683
2684 if (mParent && !mParent->TransferDataToWindow())
2685 {
2686 return false;
2687 }
2688
2689 return true;
2690 }
2691
TransferDataFromWindow()2692 bool LV2Effect::TransferDataFromWindow()
2693 {
2694 if (!mParent->Validate() || !mParent->TransferDataFromWindow())
2695 {
2696 return false;
2697 }
2698
2699 return true;
2700 }
2701
SetSlider(const LV2ControlPortPtr & port)2702 void LV2Effect::SetSlider(const LV2ControlPortPtr & port)
2703 {
2704 float lo = port->mLo;
2705 float hi = port->mHi;
2706 float val = port->mTmp;
2707
2708 if (port->mLogarithmic)
2709 {
2710 lo = logf(lo);
2711 hi = logf(hi);
2712 val = logf(val);
2713 }
2714
2715 port->mCtrl.slider->SetValue(lrintf((val - lo) / (hi - lo) * 1000.0));
2716 }
2717
OnTrigger(wxCommandEvent & evt)2718 void LV2Effect::OnTrigger(wxCommandEvent &evt)
2719 {
2720 auto & port = mControlPorts[evt.GetId() - ID_Triggers];
2721
2722 port->mVal = port->mDef;
2723 }
2724
OnToggle(wxCommandEvent & evt)2725 void LV2Effect::OnToggle(wxCommandEvent &evt)
2726 {
2727 auto & port = mControlPorts[evt.GetId() - ID_Toggles];
2728
2729 port->mVal = evt.GetInt() ? 1.0 : 0.0;
2730 }
2731
OnChoice(wxCommandEvent & evt)2732 void LV2Effect::OnChoice(wxCommandEvent &evt)
2733 {
2734 auto & port = mControlPorts[evt.GetId() - ID_Choices];
2735
2736 port->mVal = port->mScaleValues[evt.GetInt()];
2737 }
2738
OnText(wxCommandEvent & evt)2739 void LV2Effect::OnText(wxCommandEvent &evt)
2740 {
2741 auto & port = mControlPorts[evt.GetId() - ID_Texts];
2742
2743 if (port->mText->GetValidator()->TransferFromWindow())
2744 {
2745 port->mVal = port->mSampleRate ? port->mTmp / mSampleRate : port->mTmp;
2746
2747 SetSlider(port);
2748 }
2749 }
2750
OnSlider(wxCommandEvent & evt)2751 void LV2Effect::OnSlider(wxCommandEvent &evt)
2752 {
2753 auto & port = mControlPorts[evt.GetId() - ID_Sliders];
2754
2755 float lo = port->mLo;
2756 float hi = port->mHi;
2757
2758 if (port->mLogarithmic)
2759 {
2760 lo = logf(lo);
2761 hi = logf(hi);
2762 }
2763
2764 port->mTmp = (((float) evt.GetInt()) / 1000.0) * (hi - lo) + lo;
2765 port->mTmp = port->mLogarithmic ? expf(port->mTmp) : port->mTmp;
2766
2767 port->mTmp = port->mTmp < port->mLo ? port->mLo : port->mTmp;
2768 port->mTmp = port->mTmp > port->mHi ? port->mHi : port->mTmp;
2769
2770 port->mVal = port->mSampleRate ? port->mTmp / mSampleRate : port->mTmp;
2771
2772 port->mText->GetValidator()->TransferToWindow();
2773 }
2774
OnTimer(wxTimerEvent & evt)2775 void LV2Effect::OnTimer(wxTimerEvent &evt)
2776 {
2777 evt.Skip();
2778
2779 if (mExternalWidget)
2780 {
2781 LV2_EXTERNAL_UI_RUN(mExternalWidget);
2782 }
2783 }
2784
OnIdle(wxIdleEvent & evt)2785 void LV2Effect::OnIdle(wxIdleEvent &evt)
2786 {
2787 evt.Skip();
2788
2789 if (!mSuilInstance)
2790 {
2791 return;
2792 }
2793
2794 if (mExternalUIClosed)
2795 {
2796 mExternalUIClosed = false;
2797 mDialog->Close();
2798 return;
2799 }
2800
2801 if (mUIIdleInterface)
2802 {
2803 SuilHandle handle = suil_instance_get_handle(mSuilInstance);
2804 if (mUIIdleInterface->idle(handle))
2805 {
2806 if (mUIShowInterface)
2807 {
2808 mUIShowInterface->hide(handle);
2809 }
2810 mDialog->Close();
2811 return;
2812 }
2813 }
2814
2815 if (mControlOut)
2816 {
2817 ZixRing *ring = mControlOut->mRing;
2818
2819 LV2_Atom *atom = (LV2_Atom *) malloc(mControlOut->mMinimumSize);
2820 if (atom)
2821 {
2822 while (zix_ring_read(ring, atom, sizeof(LV2_Atom)))
2823 {
2824 uint32_t size = lv2_atom_total_size(atom);
2825
2826 if (size < mControlOut->mMinimumSize)
2827 {
2828 zix_ring_read(ring, LV2_ATOM_CONTENTS(LV2_Atom, atom), atom->size);
2829 suil_instance_port_event(mSuilInstance,
2830 mControlOut->mIndex,
2831 size,
2832 urid_EventTransfer,
2833 atom);
2834 }
2835 else
2836 {
2837 zix_ring_skip(ring, atom->size);
2838 wxLogError(wxT("LV2 sequence buffer overflow"));
2839 }
2840 }
2841 free(atom);
2842 }
2843 }
2844
2845 for (auto & port : mControlPorts)
2846 {
2847 // Let UI know that a port's value has changed
2848 if (port->mVal != port->mLst)
2849 {
2850 suil_instance_port_event(mSuilInstance,
2851 port->mIndex,
2852 sizeof(port->mVal),
2853 0,
2854 &port->mVal);
2855 port->mLst = port->mVal;
2856 }
2857 }
2858 }
2859
OnSize(wxSizeEvent & evt)2860 void LV2Effect::OnSize(wxSizeEvent & evt)
2861 {
2862 evt.Skip();
2863
2864 // Don't do anything here if we're recursing
2865 if (mResizing)
2866 {
2867 return;
2868 }
2869
2870 // Indicate resizing is occurring
2871 mResizing = true;
2872
2873 // Can only resize AFTER the dialog has been completely created and
2874 // there's no need to resize if we're already at the desired size.
2875 if (mDialog && evt.GetSize() != mNativeWinLastSize)
2876 {
2877 // Save the desired size and set the native window to match
2878 mNativeWinLastSize = evt.GetSize();
2879 mNativeWin->SetMinSize(mNativeWinLastSize);
2880
2881 // Clear the minimum size of the parent window to allow the following
2882 // Fit() to make proper adjustments
2883 mParent->SetMinSize(wxDefaultSize);
2884
2885 #if defined(__WXGTK__)
2886 // If the user resized the native window, then we need to also
2887 // clear the dialogs minimum size. If this isn't done, the dialog
2888 // will not resize properly when going from a larger size to a smaller
2889 // size (due to the minimum size constraint).
2890 //
2891 // In this case, mResized has been set by the "size_request()" function
2892 // to indicate that this is a plugin generated resize request.
2893 if (mResized)
2894 {
2895 mDialog->SetMinSize(wxDefaultSize);
2896 }
2897
2898 // Resize dialog
2899 mDialog->Fit();
2900
2901 // Reestablish the minimum (and maximum) now that the dialog
2902 // has is desired size.
2903 if (mResized)
2904 {
2905 mDialog->SetMinSize(mDialog->GetSize());
2906 if (mNoResize)
2907 {
2908 mDialog->SetMaxSize(mDialog->GetSize());
2909 }
2910 }
2911
2912 // Tell size_request() that the native window was just resized.
2913 mResized = true;
2914 #else
2915 // Resize the dialog to fit its content.
2916 mDialog->Fit();
2917 #endif
2918 }
2919
2920 // No longer resizing
2921 mResizing = false;
2922 }
2923
2924 // ============================================================================
2925 // Feature handlers
2926 // ============================================================================
2927
2928 // static callback
uri_to_id(LV2_URI_Map_Callback_Data callback_data,const char * WXUNUSED (map),const char * uri)2929 uint32_t LV2Effect::uri_to_id(LV2_URI_Map_Callback_Data callback_data,
2930 const char *WXUNUSED(map),
2931 const char *uri)
2932 {
2933 return ((LV2Effect *) callback_data)->URID_Map(uri);
2934 }
2935
2936 // static callback
urid_map(LV2_URID_Map_Handle handle,const char * uri)2937 LV2_URID LV2Effect::urid_map(LV2_URID_Map_Handle handle, const char *uri)
2938 {
2939 return ((LV2Effect *) handle)->URID_Map(uri);
2940 }
2941
URID_Map(const char * uri)2942 LV2_URID LV2Effect::URID_Map(const char *uri)
2943 {
2944 LV2_URID urid;
2945
2946 urid = Lookup_URI(gURIDMap, uri, false);
2947 if (urid > 0)
2948 {
2949 return urid;
2950 }
2951
2952 urid = Lookup_URI(mURIDMap, uri);
2953 if (urid > 0)
2954 {
2955 return urid + gURIDMap.size();
2956 }
2957
2958 return 0;
2959 }
2960
Lookup_URI(URIDMap & map,const char * uri,bool add)2961 LV2_URID LV2Effect::Lookup_URI(URIDMap & map, const char *uri, bool add)
2962 {
2963 size_t ndx = map.size();
2964 for (size_t i = 0; i < ndx; i++)
2965 {
2966 if (strcmp(map[i].get(), uri) == 0)
2967 {
2968 return i + 1;
2969 }
2970 }
2971
2972 if (add)
2973 {
2974 // Almost all compilers have strdup(), but VC++ and MinGW call it _strdup().
2975 map.push_back(MallocString<>(wxCRT_StrdupA(uri)));
2976 return ndx + 1;
2977 }
2978
2979 return 0;
2980 }
2981
2982 // static callback
urid_unmap(LV2_URID_Unmap_Handle handle,LV2_URID urid)2983 const char *LV2Effect::urid_unmap(LV2_URID_Unmap_Handle handle, LV2_URID urid)
2984 {
2985 return ((LV2Effect *) handle)->URID_Unmap(urid);
2986 }
2987
URID_Unmap(LV2_URID urid)2988 const char *LV2Effect::URID_Unmap(LV2_URID urid)
2989 {
2990 if (urid > 0)
2991 {
2992 if (urid <= (LV2_URID) gURIDMap.size())
2993 {
2994 return mURIDMap[urid - 1].get();
2995 }
2996
2997 urid -= gURIDMap.size();
2998
2999 if (urid <= (LV2_URID) mURIDMap.size())
3000 {
3001 return mURIDMap[urid - 1].get();
3002 }
3003 }
3004
3005 return NULL;
3006 }
3007
3008 // static callback
log_printf(LV2_Log_Handle handle,LV2_URID type,const char * fmt,...)3009 int LV2Effect::log_printf(LV2_Log_Handle handle, LV2_URID type, const char *fmt, ...)
3010 {
3011 va_list ap;
3012 int len;
3013
3014 va_start(ap, fmt);
3015 len = ((LV2Effect *) handle)->LogVPrintf(type, fmt, ap);
3016 va_end(ap);
3017
3018 return len;
3019 }
3020
3021 // static callback
log_vprintf(LV2_Log_Handle handle,LV2_URID type,const char * fmt,va_list ap)3022 int LV2Effect::log_vprintf(LV2_Log_Handle handle, LV2_URID type, const char *fmt, va_list ap)
3023 {
3024 return ((LV2Effect *) handle)->LogVPrintf(type, fmt, ap);
3025 }
3026
LogVPrintf(LV2_URID type,const char * fmt,va_list ap)3027 int LV2Effect::LogVPrintf(LV2_URID type, const char *fmt, va_list ap)
3028 {
3029 long level = wxLOG_Error;
3030
3031 if (type == urid_Error)
3032 {
3033 level = wxLOG_Error;
3034 }
3035 else if (type == urid_Note)
3036 {
3037 level = wxLOG_Info;
3038 }
3039 else if (type == urid_Trace)
3040 {
3041 level = wxLOG_Trace;
3042 }
3043 else if (type == urid_Warning)
3044 {
3045 level = wxLOG_Warning;
3046 }
3047 else
3048 {
3049 level = wxLOG_Message;
3050 }
3051
3052 char *msg = NULL;
3053 int len = wxCRT_VsnprintfA(msg, 0, fmt, ap);
3054
3055 msg = (char *) malloc(len + 1);
3056 if (msg)
3057 {
3058 wxCRT_VsnprintfA(msg, len, fmt, ap);
3059
3060 wxString text(msg);
3061
3062 wxLogGeneric(level, wxT("%s: %s"), GetSymbol().Msgid().Translation(), text);
3063
3064 free(msg);
3065 }
3066
3067 return len;
3068 }
3069
3070 // static callback
ui_resize(LV2UI_Feature_Handle handle,int width,int height)3071 int LV2Effect::ui_resize(LV2UI_Feature_Handle handle, int width, int height)
3072 {
3073 return ((LV2Effect *) handle)->UIResize(width, height);
3074 }
3075
UIResize(int width,int height)3076 int LV2Effect::UIResize(int width, int height)
3077 {
3078 // Queue a wxSizeEvent to resize the plugins UI
3079 if (mNativeWin)
3080 {
3081 wxSizeEvent sw(wxSize(width, height));
3082 sw.SetEventObject(mNativeWin);
3083 mNativeWin->GetEventHandler()->AddPendingEvent(sw);
3084 }
3085 // The window hasn't been created yet, so record the desired size
3086 else
3087 {
3088 mNativeWinInitialSize = wxSize(width, height);
3089 }
3090
3091 return 0;
3092 }
3093
3094 // static callback
ui_closed(LV2UI_Controller controller)3095 void LV2Effect::ui_closed(LV2UI_Controller controller)
3096 {
3097 return ((LV2Effect *) controller)->UIClosed();
3098 }
3099
UIClosed()3100 void LV2Effect::UIClosed()
3101 {
3102 mExternalUIClosed = true;
3103
3104 return;
3105 }
3106
3107 // static callback
suil_port_write_func(SuilController controller,uint32_t port_index,uint32_t buffer_size,uint32_t protocol,const void * buffer)3108 void LV2Effect::suil_port_write_func(SuilController controller,
3109 uint32_t port_index,
3110 uint32_t buffer_size,
3111 uint32_t protocol,
3112 const void *buffer)
3113 {
3114 ((LV2Effect *) controller)->SuilPortWrite(port_index, buffer_size, protocol, buffer);
3115 }
3116
SuilPortWrite(uint32_t port_index,uint32_t buffer_size,uint32_t protocol,const void * buffer)3117 void LV2Effect::SuilPortWrite(uint32_t port_index,
3118 uint32_t buffer_size,
3119 uint32_t protocol,
3120 const void *buffer)
3121 {
3122 // Handle implicit floats
3123 if (protocol == 0 && buffer_size == sizeof(float))
3124 {
3125 auto it = mControlPortMap.find(port_index);
3126 if (it != mControlPortMap.end())
3127 {
3128 it->second->mVal = *((const float *) buffer);
3129 }
3130 }
3131 // Handle event transfers
3132 else if (protocol == urid_EventTransfer)
3133 {
3134 if (mControlIn && port_index == mControlIn->mIndex)
3135 {
3136 zix_ring_write(mControlIn->mRing, buffer, buffer_size);
3137 }
3138 }
3139
3140 return;
3141 }
3142
3143 // static callback
suil_port_index_func(SuilController controller,const char * port_symbol)3144 uint32_t LV2Effect::suil_port_index_func(SuilController controller,
3145 const char *port_symbol)
3146 {
3147 return ((LV2Effect *) controller)->SuilPortIndex(port_symbol);
3148 }
3149
SuilPortIndex(const char * port_symbol)3150 uint32_t LV2Effect::SuilPortIndex(const char *port_symbol)
3151 {
3152 for (size_t i = 0, cnt = lilv_plugin_get_num_ports(mPlug); i < cnt; i++)
3153 {
3154 const LilvPort *port = lilv_plugin_get_port_by_index(mPlug, i);
3155 if (strcmp(port_symbol, lilv_node_as_string(lilv_port_get_symbol(mPlug, port))) == 0)
3156 {
3157 return lilv_port_get_index(mPlug, port);
3158 }
3159 }
3160
3161 return LV2UI_INVALID_PORT_INDEX;
3162 }
3163
3164 // static callback
get_value_func(const char * port_symbol,void * user_data,uint32_t * size,uint32_t * type)3165 const void *LV2Effect::get_value_func(const char *port_symbol,
3166 void *user_data,
3167 uint32_t *size,
3168 uint32_t *type)
3169 {
3170 return ((LV2Effect *) user_data)->GetPortValue(port_symbol, size, type);
3171 }
3172
GetPortValue(const char * port_symbol,uint32_t * size,uint32_t * type)3173 const void *LV2Effect::GetPortValue(const char *port_symbol,
3174 uint32_t *size,
3175 uint32_t *type)
3176 {
3177 wxString symbol = wxString::FromUTF8(port_symbol);
3178
3179 for (auto & port : mControlPorts)
3180 {
3181 if (port->mSymbol == symbol)
3182 {
3183 *size = sizeof(float);
3184 *type = urid_Float;
3185 return (void *) &port->mVal;
3186 }
3187 }
3188
3189 *size = 0;
3190 *type = 0;
3191
3192 return NULL;
3193 }
3194
3195 // static callback
set_value_func(const char * port_symbol,void * user_data,const void * value,uint32_t size,uint32_t type)3196 void LV2Effect::set_value_func(const char *port_symbol,
3197 void *user_data,
3198 const void *value,
3199 uint32_t size,
3200 uint32_t type)
3201 {
3202 ((LV2Effect *) user_data)->SetPortValue(port_symbol, value, size, type);
3203 }
3204
SetPortValue(const char * port_symbol,const void * value,uint32_t size,uint32_t type)3205 void LV2Effect::SetPortValue(const char *port_symbol,
3206 const void *value,
3207 uint32_t size,
3208 uint32_t type)
3209 {
3210 wxString symbol = wxString::FromUTF8(port_symbol);
3211
3212 for (auto & port : mControlPorts)
3213 {
3214 if (port->mSymbol == symbol)
3215 {
3216 if (type == urid_Bool && size == sizeof(bool))
3217 {
3218 port->mVal = (float) (*((const bool *) value)) ? 1.0f : 0.0f;
3219 }
3220 else if (type == urid_Double && size == sizeof(double))
3221 {
3222 port->mVal = (float) (*((const double *) value));
3223 }
3224 else if (type == urid_Float && size == sizeof(float))
3225 {
3226 port->mVal = (float) (*((const float *) value));
3227 }
3228 else if (type == urid_Int && size == sizeof(int32_t))
3229 {
3230 port->mVal = (float) (*((const int32_t *) value));
3231 }
3232 else if (type == urid_Long && size == sizeof(int64_t))
3233 {
3234 port->mVal = (float) (*((const int64_t *) value));
3235 }
3236
3237 break;
3238 }
3239 }
3240 }
3241
3242 #if defined(__WXGTK__)
3243 // static callback
3244 //
3245 // Need to queue a wxSizeEvent when the native window gets resized outside of
3246 // WX control. Many of the x42 LV2 plugins can resize themselves when changing
3247 // the scale factor. (e.g., open "x42-dpl" effect and right click to change scaling)
size_request(GtkWidget * widget,GtkRequisition * requisition,LV2Effect * effect)3248 void LV2Effect::size_request(GtkWidget *widget, GtkRequisition *requisition, LV2Effect *effect)
3249 {
3250 effect->SizeRequest(widget, requisition);
3251 }
3252
SizeRequest(GtkWidget * widget,GtkRequisition * requisition)3253 void LV2Effect::SizeRequest(GtkWidget *widget, GtkRequisition *requisition)
3254 {
3255 // Don't do anything if the OnSize() method is active
3256 if (!mResizing)
3257 {
3258 // If the OnSize() routine has processed an event, mResized will be true,
3259 // so just set the widgets size.
3260 if (mResized)
3261 {
3262 gtk_widget_set_size_request(widget, mNativeWinLastSize.x, mNativeWinLastSize.y);
3263 mResized = false;
3264 }
3265 // Otherwise, the plugin has resized the widget and we need to let WX know
3266 // about it.
3267 else if (mNativeWin)
3268 {
3269 mResized = true;
3270 wxSizeEvent se(wxSize(requisition->width, requisition->height));
3271 se.SetEventObject(mNativeWin);
3272 mNativeWin->GetEventHandler()->AddPendingEvent(se);
3273 }
3274 }
3275 }
3276 #endif
3277
LV2Wrapper(LV2Effect * effect)3278 LV2Wrapper::LV2Wrapper(LV2Effect *effect)
3279 : mEffect(effect)
3280 {
3281 mInstance = NULL;
3282 mHandle = NULL;
3283 mOptionsInterface = NULL;
3284 mStateInterface = NULL;
3285 mWorkerInterface = NULL;
3286 mWorkerSchedule = {};
3287 mFreeWheeling = false;
3288 mLatency = 0.0;
3289 mStopWorker = false;
3290 }
3291
~LV2Wrapper()3292 LV2Wrapper::~LV2Wrapper()
3293 {
3294 if (mInstance)
3295 {
3296 wxThread *thread = GetThread();
3297 if (thread && thread->IsAlive())
3298 {
3299 mStopWorker = true;
3300
3301 LV2Work work = {0, NULL};
3302 mRequests.Post(work);
3303
3304 thread->Wait();
3305 }
3306
3307 if (mEffect->mActivated)
3308 {
3309 lilv_instance_deactivate(mInstance);
3310 mEffect->mActivated = false;
3311 }
3312
3313 lilv_instance_free(mInstance);
3314 mInstance = NULL;
3315 }
3316 }
3317
Instantiate(const LilvPlugin * plugin,double sampleRate,std::vector<std::unique_ptr<LV2_Feature>> & features)3318 LilvInstance *LV2Wrapper::Instantiate(const LilvPlugin *plugin,
3319 double sampleRate,
3320 std::vector<std::unique_ptr<LV2_Feature>> & features)
3321 {
3322 if (mEffect->mWantsWorkerInterface)
3323 {
3324 // Remove terminator
3325 features.pop_back();
3326
3327 mWorkerSchedule.handle = this;
3328 mWorkerSchedule.schedule_work = LV2Wrapper::schedule_work;
3329 mEffect->AddFeature(LV2_WORKER__schedule, &mWorkerSchedule);
3330
3331 mEffect->AddFeature(NULL, NULL);
3332 }
3333
3334 #if defined(__WXMSW__)
3335 // Plugins may have dependencies that need to be loaded from the same path
3336 // as the main DLL, so add this plugin's path to the DLL search order.
3337 const LilvNode *const libNode = lilv_plugin_get_library_uri(plugin);
3338 const char *const libUri = lilv_node_as_uri(libNode);
3339 char *libPath = lilv_file_uri_parse(libUri, NULL);
3340 wxString path = wxPathOnly(libPath);
3341 SetDllDirectory(path.c_str());
3342 lilv_free(libPath);
3343 #endif
3344
3345 mInstance = lilv_plugin_instantiate(plugin,
3346 sampleRate,
3347 reinterpret_cast<LV2_Feature **>(features.data()));
3348
3349 #if defined(__WXMSW__)
3350 SetDllDirectory(NULL);
3351 #endif
3352
3353 if (mEffect->mWantsWorkerInterface)
3354 {
3355 // Remove terminator
3356 features.pop_back();
3357
3358 // Remove the worker interface feature
3359 features.pop_back();
3360
3361 // Re-add terminator
3362 mEffect->AddFeature(NULL, NULL);
3363 }
3364
3365 if (!mInstance)
3366 {
3367 return NULL;
3368 }
3369
3370 mHandle = lilv_instance_get_handle(mInstance);
3371
3372 mOptionsInterface = (LV2_Options_Interface *)
3373 lilv_instance_get_extension_data(mInstance, LV2_OPTIONS__interface);
3374
3375 mStateInterface = (LV2_State_Interface *)
3376 lilv_instance_get_extension_data(mInstance, LV2_STATE__interface);
3377
3378 mWorkerInterface = (LV2_Worker_Interface *)
3379 lilv_instance_get_extension_data(mInstance, LV2_WORKER__interface);
3380
3381 if (mEffect->mLatencyPort >= 0)
3382 {
3383 lilv_instance_connect_port(mInstance, mEffect->mLatencyPort, &mLatency);
3384 }
3385
3386 if (mWorkerInterface)
3387 {
3388 if (CreateThread() == wxTHREAD_NO_ERROR)
3389 {
3390 GetThread()->Run();
3391 }
3392
3393 }
3394
3395 return mInstance;
3396 }
3397
GetInstance()3398 LilvInstance *LV2Wrapper::GetInstance()
3399 {
3400 return mInstance;
3401 }
3402
GetHandle()3403 LV2_Handle LV2Wrapper::GetHandle()
3404 {
3405 return mHandle;
3406 }
3407
GetLatency()3408 float LV2Wrapper::GetLatency()
3409 {
3410 return mLatency;
3411 }
3412
SetFreeWheeling(bool enable)3413 void LV2Wrapper::SetFreeWheeling(bool enable)
3414 {
3415 mFreeWheeling = enable;
3416 }
3417
SetSampleRate()3418 void LV2Wrapper::SetSampleRate()
3419 {
3420 if (mEffect->mSupportsSampleRate && mOptionsInterface && mOptionsInterface->set)
3421 {
3422 LV2_Options_Option options[2] = {};
3423
3424 memcpy(&options,
3425 &mEffect->mOptions[mEffect->mSampleRateOption],
3426 sizeof(mEffect->mOptions[0]));
3427
3428 mOptionsInterface->set(mHandle, options);
3429 }
3430 }
3431
SetBlockSize()3432 void LV2Wrapper::SetBlockSize()
3433 {
3434 if (mEffect->mSupportsNominalBlockLength && mOptionsInterface && mOptionsInterface->set)
3435 {
3436 LV2_Options_Option options[2] = {};
3437 memcpy(&options,
3438 &mEffect->mOptions[mEffect->mBlockSizeOption],
3439 sizeof(mEffect->mOptions[0]));
3440
3441 mOptionsInterface->set(mHandle, options);
3442 }
3443 }
3444
ConnectPorts(float ** inbuf,float ** outbuf)3445 void LV2Wrapper::ConnectPorts(float **inbuf, float **outbuf)
3446 {
3447 }
3448
Entry()3449 void *LV2Wrapper::Entry()
3450 {
3451 LV2Work work;
3452
3453 while (mRequests.Receive(work) == wxMSGQUEUE_NO_ERROR)
3454 {
3455 if (mStopWorker)
3456 {
3457 break;
3458 }
3459
3460 mWorkerInterface->work(mHandle,
3461 respond,
3462 this,
3463 work.size,
3464 work.data);
3465 }
3466
3467 return (void *) 0;
3468 }
3469
SendResponses()3470 void LV2Wrapper::SendResponses()
3471 {
3472 if (mWorkerInterface)
3473 {
3474 LV2Work work;
3475
3476 while (mResponses.ReceiveTimeout(0, work) == wxMSGQUEUE_NO_ERROR)
3477 {
3478 mWorkerInterface->work_response(mHandle, work.size, work.data);
3479 }
3480
3481 if (mWorkerInterface->end_run)
3482 {
3483 mWorkerInterface->end_run(mHandle);
3484 }
3485 }
3486 }
3487
3488 // static callback
schedule_work(LV2_Worker_Schedule_Handle handle,uint32_t size,const void * data)3489 LV2_Worker_Status LV2Wrapper::schedule_work(LV2_Worker_Schedule_Handle handle,
3490 uint32_t size,
3491 const void *data)
3492 {
3493 return ((LV2Wrapper *) handle)->ScheduleWork(size, data);
3494 }
3495
ScheduleWork(uint32_t size,const void * data)3496 LV2_Worker_Status LV2Wrapper::ScheduleWork(uint32_t size, const void *data)
3497 {
3498 if (mFreeWheeling)
3499 {
3500 return mWorkerInterface->work(mHandle,
3501 respond,
3502 this,
3503 size,
3504 data);
3505 }
3506
3507 LV2Work work = {size, data};
3508
3509 mRequests.Post(work);
3510
3511 return LV2_WORKER_SUCCESS;
3512 }
3513
3514 // static callback
respond(LV2_Worker_Respond_Handle handle,uint32_t size,const void * data)3515 LV2_Worker_Status LV2Wrapper::respond(LV2_Worker_Respond_Handle handle,
3516 uint32_t size,
3517 const void *data)
3518 {
3519 return ((LV2Wrapper *) handle)->Respond(size, data);
3520 }
3521
Respond(uint32_t size,const void * data)3522 LV2_Worker_Status LV2Wrapper::Respond(uint32_t size, const void *data)
3523 {
3524 LV2Work work = {size, data};
3525
3526 mResponses.Post(work);
3527
3528 return LV2_WORKER_SUCCESS;
3529 }
3530
3531 #endif
3532
3533