Audacity 3.2.0
LadspaEffect.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 LadspaEffect.cpp
6
7 Dominic Mazzoni
8
9 This class implements a LADSPA Plug-in effect.
10
11*******************************************************************//****************************************************************//*******************************************************************/
23
24
25
26#include "LadspaEffect.h" // This class's header file
27#include "SampleCount.h"
28#include "ConfigInterface.h"
29
30#include <float.h>
31#include <thread>
32
33#if !defined(__WXMSW__)
34#include <dlfcn.h>
35
36#ifndef RTLD_DEEPBIND
37#define RTLD_DEEPBIND 0
38#endif
39#endif
40
41#include <wx/setup.h> // for wxUSE_* macros
42#include <wx/wxprec.h>
43#include <wx/button.h>
44#include <wx/checkbox.h>
45#include <wx/dcclient.h>
46#include <wx/filename.h>
47#include <wx/log.h>
48#include <wx/menu.h>
49#include <wx/sizer.h>
50#include <wx/slider.h>
51#include <wx/statbox.h>
52#include <wx/stattext.h>
53#include <wx/textctrl.h>
54#include <wx/tokenzr.h>
55#include <wx/intl.h>
56#include <wx/scrolwin.h>
57#include <wx/version.h>
58
59#include "AudacityException.h"
60#include "FileNames.h"
61#include "../../ShuttleGui.h"
62#include "../../widgets/NumericTextCtrl.h"
63#include "../../widgets/valnum.h"
64#include "../../widgets/wxPanelWrapper.h"
65#include "ModuleManager.h"
66
67#if wxUSE_ACCESSIBILITY
68#include "../../widgets/WindowAccessible.h"
69#endif
70
71// ============================================================================
72// List of effects that ship with Audacity. These will be autoregistered.
73// ============================================================================
74const static wxChar *kShippedEffects[] =
75{
76 wxT("sc4_1882.dll"),
77};
78
79// ============================================================================
80// Tolerance to be used when comparing control values.
81constexpr float ControlValueTolerance = 1.0e-5f;
82// ============================================================================
83
84// ============================================================================
85// Module registration entry point
86//
87// This is the symbol that Audacity looks for when the module is built as a
88// dynamic library.
89//
90// When the module is builtin to Audacity, we use the same function, but it is
91// declared static so as not to clash with other builtin modules.
92// ============================================================================
94{
95 // Create and register the importer
96 // Trust the module manager not to leak this
97 return std::make_unique<LadspaEffectsModule>();
98}
99
100// ============================================================================
101// Register this as a builtin module
102// ============================================================================
104
106//
107// LadspaEffectsModule
108//
110
112{
113}
114
116{
117}
118
119// Don't use the template-generated MakeSettings(), which default-constructs
120// the structure. Instead allocate a number of values chosen by the plug-in
122{
123 auto result = EffectSettings::Make<LadspaEffectSettings>( mData->PortCount );
125 return result;
126}
127
129 const EffectSettings &src, EffectSettings &dst) const
130{
131 // Do not use the copy constructor of std::vector. Do an in-place rewrite
132 // of the destination vector, which will not allocate memory if dstControls
133 // began with sufficient capacity.
134 const auto portCount = mData->PortCount;
135
136 auto &srcControls = GetSettings(src).controls;
137 auto &dstControls = GetSettings(dst).controls;
138
139 assert(srcControls.size() == portCount);
140 assert(dstControls.size() == portCount);
141
142 const auto portValuesCount =
143 std::min(srcControls.size(), dstControls.size());
144
145 if (portValuesCount != portCount)
146 return false;
147
148 for (unsigned long p = 0; p < portCount; ++p)
149 {
151
152 if (!(LADSPA_IS_PORT_CONTROL(d)))
153 continue;
154
156 dstControls[p] = srcControls[p];
157 }
158
159 return true;
160}
161
162auto LadspaEffect::MakeOutputs() const -> std::unique_ptr<EffectOutputs>
163{
164 auto result = std::make_unique<LadspaEffectOutputs>();
165 result->controls.resize(mData->PortCount);
166 return result;
167}
168
169// ============================================================================
170// ComponentInterface implementation
171// ============================================================================
172
174{
175 return {};
176}
177
179{
180 /* i18n-hint: abbreviates "Linux Audio Developer's Simple Plugin API"
181 (Application programming interface)
182 */
183 return XO("LADSPA Effects");
184}
185
187{
188 return XO("The Audacity Team");
189}
190
192{
193 // This "may" be different if this were to be maintained as a separate DLL
195}
196
198{
199 return XO("Provides LADSPA Effects");
200}
201
202// ============================================================================
203// PluginProvider implementation
204// ============================================================================
205
207{
208 // Nothing to do here
209 return true;
210}
211
213{
214 // Nothing to do here
215 return;
216}
217
219{
220#if USE_LADSPA
222#else
223 return {};
224#endif
225}
226
228{
229 static FileExtensions result{{
230
231#ifdef __WXMSW__
232
233 _T("dll")
234
235#else
236
237 _T("so")
238
239 #ifdef __WXMAC__
240 // Is it correct that these are candidate plug-in files too for macOs?
241 , _T("dylib")
242 #endif
243
244#endif
245
246 }};
247 return result;
248}
249
251{
252 // To do: better choice
253 return FileNames::PlugInDir();
254}
255
257{
258 // Autoregister effects that we "think" are ones that have been shipped with
259 // Audacity. A little simplistic, but it should suffice for now.
260 auto pathList = GetSearchPaths();
261 FilePaths files;
262 TranslatableString ignoredErrMsg;
263
264 for (int i = 0; i < (int)WXSIZEOF(kShippedEffects); i++)
265 {
266 files.clear();
267 pm.FindFilesInPathList(kShippedEffects[i], pathList, files);
268 for (size_t j = 0, cnt = files.size(); j < cnt; j++)
269 {
270 if (!pm.IsPluginRegistered(files[j]))
271 {
272 // No checking for error ?
273 DiscoverPluginsAtPath(files[j], ignoredErrMsg,
275 }
276 }
277 }
278}
279
281{
282 auto pathList = GetSearchPaths();
283 FilePaths files;
284
285#if defined(__WXMAC__)
286
287 // Recursively scan for all shared objects
288 pm.FindFilesInPathList(wxT("*.so"), pathList, files, true);
289
290#elif defined(__WXMSW__)
291
292 // Recursively scan for all DLLs
293 pm.FindFilesInPathList(wxT("*.dll"), pathList, files, true);
294
295#else
296
297 // Recursively scan for all shared objects
298 pm.FindFilesInPathList(wxT("*.so"), pathList, files, true);
299
300#endif
301
302 return { files.begin(), files.end() };
303}
304
306 const PluginPath & path, TranslatableString &errMsg,
307 const RegistrationCallback &callback)
308{
309 errMsg = {};
310 // Since we now have builtin VST support, ignore the VST bridge as it
311 // causes duplicate menu entries to appear.
312 wxFileName ff(path);
313 if (ff.GetName().CmpNoCase(wxT("vst-bridge")) == 0) {
314 errMsg = XO("Audacity no longer uses vst-bridge");
315 return 0;
316 }
317
318 // As a courtesy to some plug-ins that might be bridges to
319 // open other plug-ins, we set the current working
320 // directory to be the plug-in's directory.
321 wxString envpath;
322 bool hadpath = wxGetEnv(wxT("PATH"), &envpath);
323 wxSetEnv(wxT("PATH"), ff.GetPath() + wxFILE_SEP_PATH + envpath);
324 wxString saveOldCWD = ff.GetCwd();
325 ff.SetCwd();
326
327 int index = 0;
328 int nLoaded = 0;
329 LADSPA_Descriptor_Function mainFn = NULL;
330
331#if defined(__WXMSW__)
332 wxDynamicLibrary lib;
333 if (lib.Load(path, wxDL_NOW))
334#else
335 void *lib = dlopen((const char *)path.ToUTF8(), RTLD_NOW | RTLD_LOCAL | RTLD_DEEPBIND);
336 if (lib)
337#endif
338 {
339
340#if defined(__WXMSW__)
341 wxLogNull logNo;
342
343 mainFn = (LADSPA_Descriptor_Function) lib.GetSymbol(wxT("ladspa_descriptor"));
344#else
345 mainFn = (LADSPA_Descriptor_Function) dlsym(lib, "ladspa_descriptor");
346#endif
347
348 if (mainFn) {
349 const LADSPA_Descriptor *data;
350
351 for (data = mainFn(index); data; data = mainFn(++index)) {
352 LadspaEffect effect(path, index);
353 if (effect.InitializePlugin()) {
354 ++nLoaded;
355 if (callback)
356 callback( this, &effect );
357 }
358 else
359 errMsg = XO("Could not load the library");
360 }
361 }
362 }
363 else
364 errMsg = XO("Could not load the library");
365
366#if defined(__WXMSW__)
367 if (lib.IsLoaded()) {
368 // PRL: I suspect Bug1257 -- Crash when enabling Amplio2 -- is the fault of a timing-
369 // dependent multi-threading bug in the Amplio2 library itself, in case the unload of the .dll
370 // comes too soon after the load. I saw the bug in Release builds but not Debug.
371 // A sleep of even 1 ms was enough to fix the problem for me, but let's be even more generous.
372 using namespace std::chrono;
373 std::this_thread::sleep_for(10ms);
374 lib.Unload();
375 }
376#else
377 if (lib) {
378 dlclose(lib);
379 }
380#endif
381
382 wxSetWorkingDirectory(saveOldCWD);
383 hadpath ? wxSetEnv(wxT("PATH"), envpath) : wxUnsetEnv(wxT("PATH"));
384
385 return nLoaded;
386}
387
388std::unique_ptr<ComponentInterface>
390{
391 // Acquires a resource for the application.
392 // For us, the path is two words.
393 // 1) The library's path
394 // 2) The LADSPA descriptor index
395 long index;
396 wxString realPath = path.BeforeFirst(wxT(';'));
397 path.AfterFirst(wxT(';')).ToLong(&index);
398 auto result = std::make_unique<LadspaEffect>(realPath, (int)index);
399 result->FullyInitializePlugin();
400 return result;
401}
402
404{
405 const auto realPath = path.BeforeFirst(wxT(';'));
406 return wxFileName::FileExists(realPath);
407}
408
410{
411 FilePaths pathList;
412 wxString pathVar;
413
414 // Check for the LADSPA_PATH environment variable
415 pathVar = wxString::FromUTF8(getenv("LADSPA_PATH"));
416 if (!pathVar.empty())
417 {
418 wxStringTokenizer tok(pathVar, wxPATH_SEP);
419 while (tok.HasMoreTokens())
420 {
421 pathList.push_back(tok.GetNextToken());
422 }
423 }
424
425#if defined(__WXMAC__)
426#define LADSPAPATH wxT("/Library/Audio/Plug-Ins/LADSPA")
427
428 // Look in ~/Library/Audio/Plug-Ins/LADSPA and /Library/Audio/Plug-Ins/LADSPA
429 pathList.push_back(wxGetHomeDir() + wxFILE_SEP_PATH + LADSPAPATH);
430 pathList.push_back(LADSPAPATH);
431
432#elif defined(__WXMSW__)
433
434 // No special paths...probably should look in %CommonProgramFiles%\LADSPA
435
436#else
437
438 pathList.push_back(wxGetHomeDir() + wxFILE_SEP_PATH + wxT(".ladspa"));
439#if defined(__LP64__)
440 pathList.push_back(wxT("/usr/local/lib64/ladspa"));
441 pathList.push_back(wxT("/usr/lib64/ladspa"));
442#endif
443 pathList.push_back(wxT("/usr/local/lib/ladspa"));
444 pathList.push_back(wxT("/usr/lib/ladspa"));
445 pathList.push_back(wxT(LIBDIR) wxT("/ladspa"));
446
447#endif
448
449 return pathList;
450}
451
453//
454// LadspaEffectOptionsDialog
455//
457
459{
460public:
462 wxWindow * parent, EffectDefinitionInterface &effect, bool &var);
464
466
467 void OnOk(wxCommandEvent & evt);
468
469private:
472
473 DECLARE_EVENT_TABLE()
474};
475
479
481 wxWindow * parent, EffectDefinitionInterface &effect, bool &var)
482: wxDialogWrapper(parent, wxID_ANY, XO("LADSPA Effect Options"))
483, mEffect{ effect }
484, mUseLatency{ var }
485{
486 mUseLatency = LadspaEffect::LoadUseLatency(effect);
487
488 ShuttleGui S(this, eIsCreating);
489 PopulateOrExchange(S);
490}
491
493{
494}
495
496static const wchar_t *OptionsKey = L"Options";
497static const wchar_t *UseLatencyKey = L"UseLatency";
498
500{
501 bool result{};
503 OptionsKey, UseLatencyKey, result, true /* default value */);
504 return result;
505}
506
508 const EffectDefinitionInterface &effect, bool value)
509{
510 return SetConfig(
512}
513
515{
516 S.SetBorder(5);
517 S.StartHorizontalLay(wxEXPAND, 1);
518 {
519 S.StartVerticalLay(false);
520 {
521 S.StartStatic(XO("Latency Compensation"));
522 {
523 S.AddVariableText( XO(
524"As part of their processing, some LADSPA effects must delay returning "
525"audio to Audacity. When not compensating for this delay, you will "
526"notice that small silences have been inserted into the audio. "
527"Enabling this option will provide that compensation, but it may "
528"not work for all LADSPA effects."),
529 false, 0, 650);
530
531 S.StartHorizontalLay(wxALIGN_LEFT);
532 {
533 S.TieCheckBox(XXO("Enable &compensation"),
535 }
536 S.EndHorizontalLay();
537 }
538 S.EndStatic();
539 }
540 S.EndVerticalLay();
541 }
542 S.EndHorizontalLay();
543
544 S.AddStandardButtons();
545
546 Layout();
547 Fit();
548 Center();
549}
550
551void LadspaEffectOptionsDialog::OnOk(wxCommandEvent & WXUNUSED(evt))
552{
553 if (!Validate())
554 {
555 return;
556 }
557
559 // Note this call re-visits the controls, not to create them but to fetch
560 // the values, in this case mUseLatency
562
564
565 EndModal(wxID_OK);
566}
567
568enum
569{
570 ID_Duration = 20000,
571 ID_Toggles = 21000,
572 ID_Sliders = 22000,
573 ID_Texts = 23000,
574};
575
577//
578// LadspaEffectMeter
579//
581
582class LadspaEffectMeter final : public wxWindow
583{
584public:
585 LadspaEffectMeter(wxWindow *parent, const float & val, float min, float max);
586
588 {
589 // Stop using mVal, it might be dangling now
590 mConnected = false;
591 }
592
593 virtual ~LadspaEffectMeter();
594
595private:
596 void OnErase(wxEraseEvent & evt);
597 void OnPaint(wxPaintEvent & evt);
598 void OnIdle(wxIdleEvent & evt);
599 void OnSize(wxSizeEvent & evt);
600
601private:
602 bool mConnected{ true };
603 const float & mVal;
604 float mMin;
605 float mMax;
607
608 DECLARE_EVENT_TABLE()
609};
610
611BEGIN_EVENT_TABLE(LadspaEffectMeter, wxWindow)
613 EVT_ERASE_BACKGROUND(LadspaEffectMeter::OnErase)
617
618LadspaEffectMeter::LadspaEffectMeter(wxWindow *parent, const float & val, float min, float max)
619: wxWindow{ parent, wxID_ANY, wxDefaultPosition, wxDefaultSize,
620 wxSIMPLE_BORDER },
621 mVal(val)
622{
623 mMin = min;
624 mMax = max;
625 mLastValue = -mVal;
626 SetBackgroundColour(*wxWHITE);
627 SetMinSize({ 20, 20 });
628}
629
631{
632}
633
634void LadspaEffectMeter::OnIdle(wxIdleEvent &evt)
635{
636 evt.Skip();
637 if (!mConnected)
638 return;
639 if (mLastValue != mVal)
640 Refresh(false);
641}
642
643void LadspaEffectMeter::OnErase(wxEraseEvent & WXUNUSED(evt))
644{
645 // Just ignore it to prevent flashing
646}
647
648void LadspaEffectMeter::OnPaint(wxPaintEvent & WXUNUSED(evt))
649{
650 if (!mConnected)
651 return;
652
653 wxPaintDC dc(this);
654
655 // Cache some metrics
656 wxRect r = GetClientRect();
657 wxCoord x = r.GetLeft();
658 wxCoord y = r.GetTop();
659 wxCoord w = r.GetWidth();
660 wxCoord h = r.GetHeight();
661
662 // These use unscaled value, min, and max
663 float val = mVal;
664 if (val > mMax)
665 val = mMax;
666 if (val < mMin)
667 val = mMin;
668 val -= mMin;
669
670 // Setup for erasing the background
671 dc.SetPen(*wxTRANSPARENT_PEN);
672 dc.SetBrush(wxColour(100, 100, 220));
673 dc.Clear();
674 dc.DrawRectangle(x, y, (w * (val / fabs(mMax - mMin))), h);
675
677}
678
679void LadspaEffectMeter::OnSize(wxSizeEvent & WXUNUSED(evt))
680{
681 Refresh(false);
682}
683
685
686auto LadspaEffectOutputs::Clone() const -> std::unique_ptr<EffectOutputs>
687{
688 return std::make_unique<LadspaEffectOutputs>(*this);
689}
690
692{
693 // Don't really need to modify src
694 const auto &srcValues = static_cast<LadspaEffectOutputs&>(src).controls;
695 auto &dstValues = controls;
696 assert(srcValues.size() == dstValues.size());
697 copy(srcValues.begin(), srcValues.end(), dstValues.data());
698}
699
701//
702// LadspaEffect
703//
705
706LadspaEffect::LadspaEffect(const wxString & path, int index)
707 : mPath{ path }
708 , mIndex{ index }
709{
710}
711
713{
714}
715
716// ============================================================================
717// ComponentInterface implementation
718// ============================================================================
719
721{
722 return wxString::Format(wxT("%s;%d"), mPath, mIndex);
723}
724
726{
727 return LAT1CTOWX(mData->Name);
728}
729
731{
732 return { LAT1CTOWX(mData->Maker) };
733}
734
736{
737 return "n/a";
738}
739
741{
742 return Verbatim( LAT1CTOWX(mData->Copyright) );
743}
744
745// ============================================================================
746// EffectDefinitionInterface implementation
747// ============================================================================
748
750{
751 if (mAudioIns == 0 && mAudioOuts == 0)
752 {
753 return EffectTypeTool;
754 }
755
756 if (mAudioIns == 0)
757 {
758 return EffectTypeGenerate;
759 }
760
761 if (mAudioOuts == 0)
762 {
763 return EffectTypeAnalyze;
764 }
765
766 return EffectTypeProcess;
767}
768
770{
772}
773
775{
776 return mInteractive;
777}
778
780{
781 return false;
782}
783
785{
786 return GetType() == EffectTypeProcess
789}
790
792{
793 return mNumInputControls > 0;
794}
795
796namespace {
797std::pair<float, float>
798InputControlPortBounds(const LADSPA_PortRangeHint &hint, double sampleRate)
799{
800 // Find lower and upper bound values for ths hint
801 const auto multiplier =
802 LADSPA_IS_HINT_SAMPLE_RATE(hint.HintDescriptor) ? sampleRate : 1.0;
803 return { hint.LowerBound * multiplier, hint.UpperBound * multiplier };
804}
806 const LADSPA_PortRangeHint &hint, float val, float lower, float upper)
807{
808 if (LADSPA_IS_HINT_BOUNDED_BELOW(hint.HintDescriptor) && val < lower)
809 val = lower;
810 if (LADSPA_IS_HINT_BOUNDED_ABOVE(hint.HintDescriptor) && val > upper)
811 val = upper;
812 return val;
813}
815 const LADSPA_PortRangeHint &hint, double sampleRate)
816{
817 // See comments in library header ladspa.h about interpretation of macros
818 const auto bounds = InputControlPortBounds(hint, sampleRate);
819
820 // Function to find low, middle, or high default values
821 const auto combine = [bounds,
823 ](float lowWeight, float highWeight){
824 auto [lower, upper] = bounds;
825 return logarithmic
826 ? exp(log(lower) * lowWeight + log(upper) * highWeight)
827 : lower * lowWeight + upper * highWeight;
828 };
829
830 auto [lower, upper] = bounds;
831 auto val = 1.0f;
832 // Four bits of the descriptor describe mutually exclusive cases
835 default:
836 break;
838 val = lower; break;
840 val = combine(0.75, 0.25); break;
842 val = combine(0.5, 0.5); break;
844 val = combine(0.25, 0.75); break;
846 val = upper; break;
848 val = 0.0f; break;
850 val = 1.0f; break;
852 val = 100.0f; break;
854 val = 440.0f; break;
855 }
856
857 return ClampInputControlValue(hint, val, lower, upper);
858}
859}
860
862{
863 if (!Load())
864 return false;
865
868 for (unsigned long p = 0; p < mData->PortCount; p++) {
870
871 // Collect the audio ports
872 if (LADSPA_IS_PORT_AUDIO(d)) {
874 mInputPorts[mAudioIns++] = p;
875 else if (LADSPA_IS_PORT_OUTPUT(d))
877 }
878 // Count control ports
879 else if (LADSPA_IS_PORT_CONTROL(d)) {
880 if (LADSPA_IS_PORT_INPUT(d)) {
881 mInteractive = true;
883 }
884 else if (LADSPA_IS_PORT_OUTPUT(d)) {
885 // LADSPA effects have a convention of providing latency on an output
886 // control port whose name is "latency".
887 if (strcmp(mData->PortNames[p], "latency") == 0)
888 mLatencyPort = p;
889 else {
890 mInteractive = true;
892 }
893 }
894 }
895 }
896 return true;
897}
898
900{
901 if (!InitializePlugin())
902 return false;
903
904 // Reading these values from the config file can't be done in the PluginHost
905 // process but isn't needed only for plugin discovery.
906
908 return true;
909}
910
912{
913 auto &controls = settings.controls;
914 // (Re-)initialize with right-sized vector
915 std::vector<float>(mData->PortCount).swap(controls);
916
917 for (unsigned long p = 0; p < mData->PortCount; ++p) {
920 // Determine the port's default value
921 controls[p] = InputControlPortDefaultValue(
923 else
924 controls[p] = 0;
925 }
926 return true;
927}
928
932{
934 bool ProcessInitialize(EffectSettings &settings, double sampleRate,
935 ChannelNames chanMap) override;
936 bool ProcessFinalize() noexcept override;
938 const float *const *inBlock, float *const *outBlock, size_t blockLen)
939 override;
940
941 SampleCount GetLatency(const EffectSettings &settings, double sampleRate)
942 const override;
943
944 bool RealtimeInitialize(EffectSettings &settings, double sampleRate)
945 override;
947 EffectOutputs *pOutputs, unsigned numChannels, float sampleRate)
948 override;
949 bool RealtimeSuspend() override;
950 bool RealtimeResume() override;
951 bool RealtimeProcessStart(MessagePackage &package) override;
952 size_t RealtimeProcess(size_t group, EffectSettings &settings,
953 const float *const *inBuf, float *const *outBuf, size_t numSamples)
954 override;
955 bool RealtimeProcessEnd(EffectSettings &settings) noexcept override;
956 bool RealtimeFinalize(EffectSettings &settings) noexcept override;
957
958 unsigned GetAudioInCount() const override;
959 unsigned GetAudioOutCount() const override;
960
961 const LadspaEffect &GetEffect() const
962 { return static_cast<const LadspaEffect &>(mProcessor); }
963
964 bool mReady{ false };
966
967 // Realtime processing
968 std::vector<LADSPA_Handle> mSlaves;
969};
970
971std::shared_ptr<EffectInstance> LadspaEffect::MakeInstance() const
972{
973 return std::make_shared<Instance>(*this);
974}
975
977 const EffectSettings &settings, double) const -> SampleCount
978{
979 auto &effect = GetEffect();
980 auto &controls = GetSettings(settings).controls;
981 if (effect.mUseLatency && effect.mLatencyPort >= 0)
982 return controls[effect.mLatencyPort];
983 return 0;
984}
985
987 EffectSettings &settings, double sampleRate, ChannelNames)
988{
989 /* Instantiate the plugin */
990 if (!mReady) {
991 auto &effect = GetEffect();
992 auto &ladspaSettings = GetSettings(settings);
993 // Destructive effect processing doesn't need output ports
994 mMaster = effect.InitInstance(sampleRate, ladspaSettings, nullptr);
995 if (!mMaster)
996 return false;
997 mReady = true;
998 }
999 return true;
1000}
1001
1003{
1004return GuardedCall<bool>([&]{
1005 if (mReady) {
1006 mReady = false;
1007 GetEffect().FreeInstance(mMaster);
1008 mMaster = nullptr;
1009 }
1010
1011 return true;
1012});
1013}
1014
1016 const float *const *inBlock, float *const *outBlock, size_t blockLen)
1017{
1018 auto &effect = GetEffect();
1019 for (unsigned i = 0; i < effect.mAudioIns; ++i)
1020 effect.mData->connect_port(mMaster, effect.mInputPorts[i],
1021 const_cast<float*>(inBlock[i]));
1022
1023 for (unsigned i = 0; i < effect.mAudioOuts; ++i)
1024 effect.mData->connect_port(mMaster, effect.mOutputPorts[i], outBlock[i]);
1025
1026 effect.mData->run(mMaster, blockLen);
1027 return blockLen;
1028}
1029
1031{
1032 return true;
1033}
1034
1036 EffectSettings &settings, EffectOutputs *pOutputs, unsigned, float sampleRate)
1037{
1038 auto &effect = GetEffect();
1039 auto &ladspaSettings = GetSettings(settings);
1040 // Connect to outputs only if this is the first processor for the track.
1041 // (What's right when a mono effect is on a stereo channel? Unclear, but
1042 // this definitely causes connection with the first channel.)
1043 auto pLadspaOutputs = mSlaves.empty()
1044 ? static_cast<LadspaEffectOutputs *>(pOutputs) : nullptr;
1045 auto slave = effect.InitInstance(sampleRate, ladspaSettings, pLadspaOutputs);
1046 if (!slave)
1047 return false;
1048 mSlaves.push_back(slave);
1049 return true;
1050}
1051
1053{
1054 return GetEffect().mAudioOuts;
1055}
1056
1058{
1059 return GetEffect().mAudioIns;
1060}
1061
1063{
1064return GuardedCall<bool>([&]{
1065 auto &effect = GetEffect();
1066 for (size_t i = 0, cnt = mSlaves.size(); i < cnt; ++i)
1067 effect.FreeInstance(mSlaves[i]);
1068 mSlaves.clear();
1069
1070 return true;
1071});
1072}
1073
1075{
1076 if (auto fn = GetEffect().mData->deactivate)
1077 for (auto &slave : mSlaves)
1078 fn(slave);
1079 return true;
1080}
1081
1083{
1084 if (auto fn = GetEffect().mData->activate)
1085 for (auto &slave : mSlaves)
1086 fn(slave);
1087 return true;
1088}
1089
1091{
1092 return true;
1093}
1094
1096 const float *const *inbuf, float *const *outbuf, size_t numSamples)
1097{
1098 if (group >= mSlaves.size())
1099 return 0;
1100
1101 auto &effect = GetEffect();
1102 for (unsigned i = 0; i < effect.mAudioIns; ++i)
1103 effect.mData->connect_port(mSlaves[group], effect.mInputPorts[i],
1104 const_cast<float*>(inbuf[i]));
1105
1106 for (unsigned i = 0; i < effect.mAudioOuts; ++i)
1107 effect.mData->connect_port(
1108 mSlaves[group], effect.mOutputPorts[i], outbuf[i]);
1109
1110 effect.mData->run(mSlaves[group], numSamples);
1111
1112 return numSamples;
1113}
1114
1116{
1117 return true;
1118}
1119
1120int LadspaEffect::ShowClientInterface(wxWindow &parent, wxDialog &dialog,
1121 EffectUIValidator *, bool forceModal)
1122{
1123 dialog.Layout();
1124 dialog.Fit();
1125 dialog.SetMinSize(dialog.GetSize());
1126
1127 if ((SupportsRealtime() || GetType() == EffectTypeAnalyze) && !forceModal)
1128 {
1129 dialog.Show();
1130 return 0;
1131 }
1132
1133 return dialog.ShowModal();
1134}
1135
1137 const EffectSettings &settings, CommandParameters & parms) const
1138{
1139 const auto &controls = GetSettings(settings).controls;
1140 for (unsigned long p = 0; p < mData->PortCount; p++) {
1143 if (!parms.Write(LAT1CTOWX(mData->PortNames[p]), controls[p]))
1144 return false;
1145 }
1146 return true;
1147}
1148
1150 const CommandParameters & parms, EffectSettings &settings) const
1151{
1152 auto &controls = GetSettings(settings).controls;
1153 for (unsigned long p = 0; p < mData->PortCount; p++) {
1155
1156 if (LADSPA_IS_PORT_CONTROL(descriptor) &&
1157 LADSPA_IS_PORT_INPUT(descriptor)) {
1158 wxString labelText = LAT1CTOWX(mData->PortNames[p]);
1159 double d = 0.0;
1160 if (!parms.Read(labelText, &d))
1161 return false;
1162 controls[p] = d;
1163 }
1164 }
1165 return true;
1166}
1167
1169 const RegistryPath & name, EffectSettings &settings) const
1170{
1171 return LoadParameters(name, settings);
1172}
1173
1175 const RegistryPath & name, const EffectSettings &settings) const
1176{
1177 return SaveParameters(name, settings);
1178}
1179
1181{
1182 return {};
1183}
1184
1186{
1187 return { nullptr };
1188}
1189
1190// ============================================================================
1191// EffectUIClientInterface Implementation
1192// ============================================================================
1193
1196 EffectSettingsAccess &access, double sampleRate, EffectType type,
1197 const LadspaEffectOutputs *pOutputs)
1198 : EffectUIValidator{ effect, access }
1199 , mSampleRate{ sampleRate }
1200 , mType{ type }
1201 // Copy settings
1202 , mSettings{ GetSettings(access.Get()) }
1203 , mpOutputs{ pOutputs }
1204 {}
1205
1206 bool UpdateUI() override;
1207 bool ValidateUI() override;
1208 void Disconnect() override;
1209
1210 void PopulateUI(ShuttleGui &S);
1211
1212 void OnCheckBox(wxCommandEvent & evt);
1213 void OnSlider(wxCommandEvent & evt);
1214 void OnTextCtrl(wxCommandEvent & evt);
1215 void RefreshControls();
1216
1217 void UpdateControl(int index, float value, float epsilon);
1218 void UpdateControls(const LadspaEffectSettings& src);
1219
1221 { return static_cast<const LadspaEffect &>(mEffect); }
1222
1223 const double mSampleRate;
1227
1229 wxWeakRef<wxDialog> mDialog;
1230 wxWindow *mParent{};
1235 std::vector<LadspaEffectMeter *> mMeters;
1236};
1237
1239{
1240 RefreshControls();
1241 return true;
1242}
1243
1245{
1246 auto &effect = GetEffect();
1247 auto &controls = mSettings.controls;
1248 auto parent = S.GetParent();
1249
1250 mParent = parent;
1251
1252 const auto &data = *effect.mData;
1253 mToggles.reinit( data.PortCount );
1254 mSliders.reinit( data.PortCount );
1255 mFields.reinit( data.PortCount, true);
1256 mLabels.reinit( data.PortCount );
1257 mMeters.resize( data.PortCount );
1258
1259 wxASSERT(mParent); // To justify safenew
1260 wxScrolledWindow *const w = safenew wxScrolledWindow(mParent,
1261 wxID_ANY,
1262 wxDefaultPosition,
1263 wxDefaultSize,
1264 wxVSCROLL | wxTAB_TRAVERSAL);
1265
1266 {
1267 auto mainSizer = std::make_unique<wxBoxSizer>(wxVERTICAL);
1268 w->SetScrollRate(0, 20);
1269
1270 // This fools NVDA into not saying "Panel" when the dialog gets focus
1271 w->SetName(wxT("\a"));
1272 w->SetLabel(wxT("\a"));
1273
1274 mainSizer->Add(w, 1, wxEXPAND);
1275 mParent->SetSizer(mainSizer.release());
1276 }
1277
1278 wxSizer *marginSizer;
1279 {
1280 auto uMarginSizer = std::make_unique<wxBoxSizer>(wxVERTICAL);
1281 marginSizer = uMarginSizer.get();
1282
1283 // Make user-adjustible input controls
1284 if (effect.mNumInputControls) {
1285 auto paramSizer = std::make_unique<wxStaticBoxSizer>(wxVERTICAL, w, _("Effect Settings"));
1286
1287 auto gridSizer = std::make_unique<wxFlexGridSizer>(5, 0, 0);
1288 gridSizer->AddGrowableCol(3);
1289
1290 wxControl *item;
1291
1292 // Add the duration control for generators
1293 if (mType == EffectTypeGenerate) {
1294 item = safenew wxStaticText(w, 0, _("Duration:"));
1295 gridSizer->Add(item, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT | wxALL, 5);
1296 auto &extra = mAccess.Get().extra;
1297 mDuration = safenew
1300 extra.GetDurationFormat(),
1301 extra.GetDuration(),
1302 mSampleRate,
1304 .AutoPos(true));
1305 mDuration->SetName( XO("Duration") );
1306 gridSizer->Add(mDuration, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
1307 gridSizer->Add(1, 1, 0);
1308 gridSizer->Add(1, 1, 0);
1309 gridSizer->Add(1, 1, 0);
1310 }
1311
1312 for (unsigned long p = 0; p < data.PortCount; ++p) {
1313 LADSPA_PortDescriptor d = data.PortDescriptors[p];
1315 {
1316 continue;
1317 }
1318
1319 wxString labelText = LAT1CTOWX(data.PortNames[p]);
1320 item = safenew wxStaticText(w, 0, wxString::Format(_("%s:"), labelText));
1321 gridSizer->Add(item, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT | wxALL, 5);
1322
1323 wxString fieldText;
1324 LADSPA_PortRangeHint hint = data.PortRangeHints[p];
1325
1327 mToggles[p] = safenew wxCheckBox(w, ID_Toggles + p, wxT(""));
1328 mToggles[p]->SetName(labelText);
1329 mToggles[p]->SetValue(controls[p] > 0);
1330 BindTo(*mToggles[p],
1331 wxEVT_COMMAND_CHECKBOX_CLICKED, &Validator::OnCheckBox);
1332 gridSizer->Add(mToggles[p], 0, wxALL, 5);
1333
1334 gridSizer->Add(1, 1, 0);
1335 gridSizer->Add(1, 1, 0);
1336 gridSizer->Add(1, 1, 0);
1337 continue;
1338 }
1339
1340 wxString bound;
1341 float lower = -FLT_MAX;
1342 float upper = FLT_MAX;
1343 bool haslo = false;
1344 bool hashi = false;
1345 bool forceint = false;
1346
1348 lower = hint.LowerBound;
1349 haslo = true;
1350 }
1351
1353 upper = hint.UpperBound;
1354 hashi = true;
1355 }
1356
1358 lower *= mSampleRate;
1359 upper *= mSampleRate;
1360 forceint = true;
1361 }
1362
1363 // Limit to the UI precision
1364 lower = ceilf(lower * 1000000.0) / 1000000.0;
1365 upper = floorf(upper * 1000000.0) / 1000000.0;
1366 controls[p] = roundf(controls[p] * 1000000.0) / 1000000.0;
1367
1368 if (haslo && controls[p] < lower)
1369 controls[p] = lower;
1370
1371 if (hashi && controls[p] > upper)
1372 controls[p] = upper;
1373
1374 // Don't specify a value at creation time. This prevents unwanted events
1375 // being sent to the OnTextCtrl() handler before the associated slider
1376 // has been created.
1377 mFields[p] = safenew wxTextCtrl(w, ID_Texts + p);
1378 mFields[p]->SetName(labelText);
1379 BindTo(*mFields[p],
1381 gridSizer->Add(mFields[p], 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
1382
1383 wxString str;
1384 if (haslo) {
1385 if (LADSPA_IS_HINT_INTEGER(hint.HintDescriptor) || forceint)
1386 {
1387 str.Printf(wxT("%d"), (int)(lower + 0.5));
1388 }
1389 else
1390 {
1392 }
1393 item = safenew wxStaticText(w, 0, str);
1394 gridSizer->Add(item, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT | wxALL, 5);
1395 }
1396 else
1397 gridSizer->Add(1, 1, 0);
1398
1399 mSliders[p] = safenew wxSliderWrapper(w, ID_Sliders + p,
1400 0, 0, 1000,
1401 wxDefaultPosition,
1402 wxSize(200, -1));
1403#if wxUSE_ACCESSIBILITY
1404 // so that name can be set on a standard control
1405 mSliders[p]->SetAccessible(safenew WindowAccessible(mSliders[p]));
1406#endif
1407 mSliders[p]->SetName(labelText);
1408 BindTo(*mSliders[p],
1409 wxEVT_COMMAND_SLIDER_UPDATED, &Validator::OnSlider);
1410 gridSizer->Add(mSliders[p], 0, wxALIGN_CENTER_VERTICAL | wxEXPAND | wxALL, 5);
1411
1412 if (hashi) {
1413 if (LADSPA_IS_HINT_INTEGER(hint.HintDescriptor) || forceint)
1414 str.Printf(wxT("%d"), (int)(upper + 0.5));
1415 else
1417 item = safenew wxStaticText(w, 0, str);
1418 gridSizer->Add(item, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_LEFT | wxALL, 5);
1419 }
1420 else
1421 gridSizer->Add(1, 1, 0);
1422
1423 if (LADSPA_IS_HINT_INTEGER(hint.HintDescriptor) || forceint) {
1424 fieldText.Printf(wxT("%d"), (int)(controls[p] + 0.5));
1425
1426 IntegerValidator<float> vld(&controls[p]);
1427 vld.SetRange(haslo ? lower : INT_MIN,
1428 hashi ? upper : INT_MAX);
1429 mFields[p]->SetValidator(vld);
1430 }
1431 else {
1432 fieldText = Internat::ToDisplayString(controls[p]);
1433
1434 // > 12 decimal places can cause rounding errors in display.
1435 FloatingPointValidator<float> vld(6, &controls[p]);
1436 vld.SetRange(lower, upper);
1437
1438 // Set number of decimal places
1439 if (upper - lower < 10.0)
1440 vld.SetStyle(NumValidatorStyle::THREE_TRAILING_ZEROES);
1441 else if (upper - lower < 100.0)
1442 vld.SetStyle(NumValidatorStyle::TWO_TRAILING_ZEROES);
1443 else
1444 vld.SetStyle(NumValidatorStyle::ONE_TRAILING_ZERO);
1445
1446 mFields[p]->SetValidator(vld);
1447 }
1448
1449 // Set the textctrl value. This will trigger an event so OnTextCtrl()
1450 // can update the slider.
1451 mFields[p]->SetValue(fieldText);
1452 }
1453
1454 paramSizer->Add(gridSizer.release(), 0, wxEXPAND | wxALL, 5);
1455 marginSizer->Add(paramSizer.release(), 0, wxEXPAND | wxALL, 5);
1456 }
1457
1458 // Make output meters
1459 if (effect.mNumOutputControls > 0) {
1460 auto paramSizer = std::make_unique<wxStaticBoxSizer>(wxVERTICAL, w, _("Effect Output"));
1461
1462 auto gridSizer = std::make_unique<wxFlexGridSizer>(2, 0, 0);
1463 gridSizer->AddGrowableCol(1);
1464
1465 wxControl *item;
1466
1467 for (unsigned long p = 0; p < data.PortCount; ++p) {
1468 LADSPA_PortDescriptor d = data.PortDescriptors[p];
1470 continue;
1471
1472 wxString labelText = LAT1CTOWX(data.PortNames[p]);
1473 item = safenew wxStaticText(
1474 w, 0, wxString::Format(_("%s:"), labelText));
1475 gridSizer->Add(
1476 item, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT | wxALL, 5);
1477
1478 //LADSPA_PortRangeHint hint = data.PortRangeHints[p];
1479
1480 wxString bound;
1481 float lower = 0.0;
1482 float upper = 1.0;
1483
1484 // Limit to the UI precision
1485 lower = ceilf(lower * 1000000.0) / 1000000.0;
1486 upper = floorf(upper * 1000000.0) / 1000000.0;
1487 controls[p] = lower;
1488
1489 // Capture const reference to output control value for later
1490 // display update
1491 static float sink;
1492 auto pOutput = mpOutputs ? &mpOutputs->controls[p] : &sink;
1493 mMeters[p] = safenew LadspaEffectMeter(
1494 w, *pOutput, lower, upper);
1495 mMeters[p]->SetLabel(labelText); // for screen readers
1496 gridSizer->Add(mMeters[p], 1, wxEXPAND | wxALIGN_CENTER_VERTICAL | wxALL, 5);
1497 }
1498
1499 paramSizer->Add(gridSizer.release(), 0, wxEXPAND | wxALL, 5);
1500 marginSizer->Add(paramSizer.release(), 0, wxEXPAND | wxALL, 5);
1501 }
1502
1503 w->SetSizer(uMarginSizer.release());
1504 }
1505
1506 w->Layout();
1507
1508 // Try to give the window a sensible default/minimum size
1509 wxSize sz1 = marginSizer->GetMinSize();
1510 wxSize sz2 = mParent->GetMinSize();
1511 w->SetMinSize( { std::min(sz1.x, sz2.x), std::min(sz1.y, sz2.y) } );
1512
1513 // And let the parent reduce to the NEW minimum if possible
1514 mParent->SetMinSize({ -1, -1 });
1515}
1516
1517std::unique_ptr<EffectUIValidator>
1520 const EffectOutputs *pOutputs)
1521{
1522 auto pValues = static_cast<const LadspaEffectOutputs *>(pOutputs);
1523 auto result = std::make_unique<Validator>(*this, access, mProjectRate,
1524 GetType(), pValues);
1525 result->PopulateUI(S);
1526 return result;
1527}
1528
1530{
1531 mAccess.ModifySettings([this](EffectSettings &settings){
1532 if (mType == EffectTypeGenerate)
1533 settings.extra.SetDuration(mDuration->GetValue());
1534 GetSettings(settings) = mSettings;
1535 return nullptr;
1536 });
1537 return true;
1538}
1539
1541{
1542 for (auto &meter : mMeters)
1543 if (meter) {
1544 meter->Disconnect();
1545 meter = nullptr;
1546 }
1547}
1548
1550{
1551 return false;
1552}
1553
1555{
1556}
1557
1559{
1560 return { nullptr };
1561}
1562
1564{
1565 return true;
1566}
1567
1569{
1571 dlg.ShowModal();
1572}
1573
1574// ============================================================================
1575// LadspaEffect Implementation
1576// ============================================================================
1577
1579{
1580 if (mLib.IsLoaded())
1581 {
1582 return true;
1583 }
1584
1585 wxFileName ff = mPath;
1586 wxString envpath;
1587 bool hadpath = wxGetEnv(wxT("PATH"), &envpath);
1588 wxSetEnv(wxT("PATH"), ff.GetPath() + wxFILE_SEP_PATH + envpath);
1589 wxString saveOldCWD = ff.GetCwd();
1590 ff.SetCwd();
1591
1592 LADSPA_Descriptor_Function mainFn = NULL;
1593
1594 if (mLib.Load(mPath, wxDL_NOW))
1595 {
1596 wxLogNull logNo;
1597
1598 mainFn = (LADSPA_Descriptor_Function) mLib.GetSymbol(wxT("ladspa_descriptor"));
1599 if (mainFn)
1600 {
1601 mData = mainFn(mIndex);
1602 return true;
1603 }
1604 }
1605
1606 if (mLib.IsLoaded())
1607 {
1608 mLib.Unload();
1609 }
1610
1611 wxSetWorkingDirectory(saveOldCWD);
1612 hadpath ? wxSetEnv(wxT("PATH"), envpath) : wxUnsetEnv(wxT("PATH"));
1613
1614 return false;
1615}
1616
1618{
1619 if (mLib.IsLoaded())
1620 {
1621 mLib.Unload();
1622 }
1623}
1624
1626 const RegistryPath & group, EffectSettings &settings) const
1627{
1628 wxString parms;
1629 if (!GetConfig(*this, PluginSettings::Private, group, wxT("Parameters"),
1630 parms, wxEmptyString))
1631 {
1632 return {};
1633 }
1634
1636 if (!eap.SetParameters(parms))
1637 {
1638 return {};
1639 }
1640
1641 if (!LoadSettings(eap, settings))
1642 return {};
1643 return { nullptr };
1644}
1645
1647 const RegistryPath & group, const EffectSettings &settings) const
1648{
1650 if (!SaveSettings(settings, eap))
1651 {
1652 return false;
1653 }
1654
1655 wxString parms;
1656 if (!eap.GetParameters(parms))
1657 {
1658 return false;
1659 }
1660
1661 return SetConfig(*this, PluginSettings::Private,
1662 group, wxT("Parameters"), parms);
1663}
1664
1666 float sampleRate, LadspaEffectSettings &settings,
1667 LadspaEffectOutputs *pOutputs) const
1668{
1669 /* Instantiate the plugin */
1670 LADSPA_Handle handle = mData->instantiate(mData, sampleRate);
1671 if (!handle)
1672 return nullptr;
1673
1674 auto &controls = settings.controls;
1675 for (unsigned long p = 0; p < mData->PortCount; ++p) {
1677 if (LADSPA_IS_PORT_CONTROL(d)) {
1678 if (LADSPA_IS_PORT_INPUT(d))
1679 mData->connect_port(handle, p, &controls[p]);
1680 else {
1681 static LADSPA_Data sink;
1682 mData->connect_port(handle, p,
1683 pOutputs ? &pOutputs->controls[p] : &sink);
1684 }
1685 }
1686 }
1687 if (mData->activate)
1688 mData->activate(handle);
1689
1690 return handle;
1691}
1692
1694{
1695 if (mData->deactivate)
1696 {
1697 mData->deactivate(handle);
1698 }
1699
1700 mData->cleanup(handle);
1701}
1702
1703void LadspaEffect::Validator::OnCheckBox(wxCommandEvent & evt)
1704{
1705 int p = evt.GetId() - ID_Toggles;
1706 // 0.5 is a half of the interval
1707 UpdateControl(p, mToggles[p]->GetValue(), 0.5f);
1708 ValidateUI();
1709}
1710
1711void LadspaEffect::Validator::OnSlider(wxCommandEvent & evt)
1712{
1713 int p = evt.GetId() - ID_Sliders;
1714
1715 float val;
1716 float lower = float(0.0);
1717 float upper = float(10.0);
1718 float range;
1719 bool forceint = false;
1720
1721 LADSPA_PortRangeHint hint = GetEffect().mData->PortRangeHints[p];
1723 lower = hint.LowerBound;
1725 upper = hint.UpperBound;
1727 lower *= mSampleRate;
1728 upper *= mSampleRate;
1729 forceint = true;
1730 }
1731
1732 range = upper - lower;
1733 val = (mSliders[p]->GetValue() / 1000.0) * range + lower;
1734 wxString str;
1735 if (LADSPA_IS_HINT_INTEGER(hint.HintDescriptor) || forceint)
1736 str.Printf(wxT("%d"), (int)(val + 0.5));
1737 else
1739
1740 mFields[p]->SetValue(str);
1741
1742 UpdateControl(p, val, ControlValueTolerance);
1743 ValidateUI();
1744}
1745
1746void LadspaEffect::Validator::OnTextCtrl(wxCommandEvent & evt)
1747{
1748 int p = evt.GetId() - ID_Texts;
1749
1750 float val;
1751 float lower = float(0.0);
1752 float upper = float(10.0);
1753 float range;
1754
1755 val = Internat::CompatibleToDouble(mFields[p]->GetValue());
1756
1757 LADSPA_PortRangeHint hint = GetEffect().mData->PortRangeHints[p];
1759 lower = hint.LowerBound;
1761 upper = hint.UpperBound;
1763 lower *= mSampleRate;
1764 upper *= mSampleRate;
1765 }
1766 range = upper - lower;
1767 if (val < lower)
1768 val = lower;
1769 if (val > upper)
1770 val = upper;
1771
1772 mSliders[p]->SetValue((int)(((val-lower)/range) * 1000.0 + 0.5));
1773
1774 UpdateControl(p, val, ControlValueTolerance);
1775 ValidateUI();
1776}
1777
1779{
1780 if (!mParent)
1781 return;
1782
1783 // Copy from the dialog
1784 UpdateControls(GetSettings(mAccess.Get()));
1785
1786 auto& controls = mSettings.controls;
1787
1788 const auto &data = *GetEffect().mData;
1789 for (unsigned long p = 0; p < data.PortCount; ++p) {
1790 LADSPA_PortDescriptor d = data.PortDescriptors[p];
1791 if (!(LADSPA_IS_PORT_CONTROL(d)))
1792 continue;
1793
1794 wxString fieldText;
1795 LADSPA_PortRangeHint hint = data.PortRangeHints[p];
1796
1797 bool forceint = false;
1799 forceint = true;
1800
1801 if (LADSPA_IS_PORT_OUTPUT(d))
1802 continue;
1803
1805 mToggles[p]->SetValue(controls[p] > 0);
1806 continue;
1807 }
1808
1809 if (LADSPA_IS_HINT_INTEGER(hint.HintDescriptor) || forceint)
1810 fieldText.Printf(wxT("%d"), (int)(controls[p] + 0.5));
1811 else
1812 fieldText = Internat::ToDisplayString(controls[p]);
1813
1814 // Set the textctrl value. This will trigger an event so OnTextCtrl()
1815 // can update the slider.
1816 mFields[p]->SetValue(fieldText);
1817 }
1818}
1819
1820void LadspaEffect::Validator::UpdateControl(int index, float value, float epsilon)
1821{
1822 auto& controls = mSettings.controls;
1823
1824 assert(index < static_cast<int>(controls.size()));
1825
1826 if (std::abs(controls[index] - value) < epsilon)
1827 return;
1828
1829 controls[index] = value;
1830 Publish({ size_t(index), value });
1831}
1832
1834{
1835 const auto& data = *GetEffect().mData;
1836
1837 for (size_t portIndex = 0, portsCount = src.controls.size();
1838 portIndex < portsCount;
1839 ++portIndex)
1840 {
1841 LADSPA_PortDescriptor d = data.PortDescriptors[portIndex];
1842
1844 continue;
1845
1846 LADSPA_PortRangeHint hint = GetEffect().mData->PortRangeHints[portIndex];
1847
1848 const bool isIntValue = (LADSPA_IS_HINT_TOGGLED(hint.HintDescriptor)) ||
1851
1852 UpdateControl(
1853 portIndex, src.controls[portIndex],
1854 isIntValue ? 0.5f : ControlValueTolerance);
1855 }
1856}
wxT("CloseDown"))
Declare abstract class AudacityException, some often-used subclasses, and GuardedCall.
END_EVENT_TABLE()
int min(int a, int b)
#define str(a)
EVT_BUTTON(wxID_NO, DependencyDialog::OnNo) EVT_BUTTON(wxID_YES
const TranslatableString name
Definition: Distortion.cpp:82
std::optional< std::unique_ptr< EffectSettingsAccess::Message > > OptionalMessage
EffectType
@ EffectTypeAnalyze
@ EffectTypeGenerate
@ EffectTypeTool
@ EffectTypeProcess
ChannelName
std::vector< PluginPath > PluginPaths
Definition: Identifier.h:215
wxString RegistryPath
Definition: Identifier.h:218
wxString PluginPath
type alias for identifying a Plugin supplied by a module, each module defining its own interpretation...
Definition: Identifier.h:214
std::vector< RegistryPath > RegistryPaths
Definition: Identifier.h:219
#define LAT1CTOWX(X)
Definition: Internat.h:160
#define XXO(s)
Definition: Internat.h:44
#define XO(s)
Definition: Internat.h:31
#define _(s)
Definition: Internat.h:75
LV2EffectSettings & GetSettings(EffectSettings &settings)
Definition: LV2Ports.h:215
DECLARE_PROVIDER_ENTRY(AudacityModule)
static const wxChar * kShippedEffects[]
static const wchar_t * UseLatencyKey
@ ID_Texts
@ ID_Duration
@ ID_Sliders
@ ID_Toggles
#define LADSPAPATH
DECLARE_BUILTIN_PROVIDER(LadspaBuiltin)
constexpr float ControlValueTolerance
static const wchar_t * OptionsKey
#define LADSPAEFFECTS_VERSION
Definition: LadspaEffect.h:29
#define LADSPAEFFECTS_FAMILY
Definition: LadspaEffect.h:33
#define safenew
Definition: MemoryX.h:10
wxEVT_COMMAND_TEXT_UPDATED
Definition: Nyquist.cpp:134
wxString FilePath
Definition: Project.h:20
@ eIsCreating
Definition: ShuttleGui.h:39
@ eIsGettingFromDialog
Definition: ShuttleGui.h:40
#define S(N)
Definition: ToChars.cpp:64
static Settings & settings()
Definition: TrackInfo.cpp:87
TranslatableString Verbatim(wxString str)
Require calls to the one-argument constructor to go through this distinct global function name.
static const auto fn
void reinit(Integral count, bool initialize=false)
Definition: MemoryX.h:57
CommandParameters, derived from wxFileConfig, is essentially doing the same things as the SettingsVis...
bool GetParameters(wxString &parms)
bool SetParameters(const wxString &parms)
ComponentInterfaceSymbol pairs a persistent string identifier used internally with an optional,...
double mProjectRate
Definition: EffectBase.h:101
EffectDefinitionInterface is a ComponentInterface that adds some basic read-only information about ef...
RealtimeSince
In which versions of Audacity was an effect realtime capable?
Base class for many of the effects in Audacity.
Definition: Effect.h:63
wxWindow * mUIParent
Definition: Effect.h:307
bool ValidateUI(EffectSettings &) override
Definition: Effect.cpp:308
Performs effect computation.
uint64_t SampleCount
Inherit to add a state variable to an EffectInstance subclass.
Hold values to send to effect output meters.
EffectUIClientInterface is an abstract base class to populate a UI and validate UI values....
Interface for transferring values from a panel of effect controls.
EffectUIClientInterface & mEffect
static LadspaEffectSettings & GetSettings(EffectSettings &settings)
Assume settings originated from MakeSettings() and copies thereof.
Definition: Effect.h:333
static wxString ToDisplayString(double numberToConvert, int digitsAfterDecimalPoint=-1)
Convert a number to a string, uses the user's locale's decimal separator.
Definition: Internat.cpp:162
static bool CompatibleToDouble(const wxString &stringToConvert, double *result)
Convert a string to a number.
Definition: Internat.cpp:134
An Effect that calls up a LADSPA plug in, i.e. many possible effects from this one class.
Definition: LadspaEffect.h:67
bool LoadSettings(const CommandParameters &parms, EffectSettings &settings) const override
Restore settings from keys and values.
LadspaEffect(const wxString &path, int index)
std::unique_ptr< EffectUIValidator > PopulateOrExchange(ShuttleGui &S, EffectInstance &instance, EffectSettingsAccess &access, const EffectOutputs *pOutputs) override
Add controls to effect panel; always succeeds.
bool CopySettingsContents(const EffectSettings &src, EffectSettings &dst) const override
Update one settings object from another.
EffectFamilySymbol GetFamily() const override
Report identifier and user-visible name of the effect protocol.
bool SupportsAutomation() const override
Whether the effect has any automatable controls.
static bool LoadUseLatency(const EffectDefinitionInterface &effect)
bool InitializePlugin()
bool SaveSettings(const EffectSettings &settings, CommandParameters &parms) const override
Store settings as keys and values.
bool CanExportPresets() override
unsigned mAudioOuts
Definition: LadspaEffect.h:169
const LADSPA_Descriptor * mData
Definition: LadspaEffect.h:157
RealtimeSince RealtimeSupport() const override
Since which version of Audacity has the effect supported realtime?
bool IsInteractive() const override
Whether the effect needs a dialog for entry of settings.
bool SaveParameters(const RegistryPath &group, const EffectSettings &settings) const
int mNumInputControls
Definition: LadspaEffect.h:173
EffectType GetType() const override
Type determines how it behaves.
OptionalMessage LoadParameters(const RegistryPath &group, EffectSettings &settings) const
wxDynamicLibrary mLib
Definition: LadspaEffect.h:156
ComponentInterfaceSymbol GetSymbol() const override
static bool SaveUseLatency(const EffectDefinitionInterface &effect, bool value)
OptionalMessage LoadUserPreset(const RegistryPath &name, EffectSettings &settings) const override
void FreeInstance(LADSPA_Handle handle) const
ArrayOf< unsigned long > mOutputPorts
Definition: LadspaEffect.h:171
const wxString mPath
Definition: LadspaEffect.h:153
bool SaveUserPreset(const RegistryPath &name, const EffectSettings &settings) const override
Save settings in the configuration file as a user-named preset.
bool HasOptions() override
wxString GetVersion() const override
void ShowOptions() override
LADSPA_Handle InitInstance(float sampleRate, LadspaEffectSettings &settings, LadspaEffectOutputs *pOutputs) const
void ExportPresets(const EffectSettings &settings) const override
RegistryPaths GetFactoryPresets() const override
Report names of factory presets.
OptionalMessage LoadFactoryPreset(int id, EffectSettings &settings) const override
TranslatableString GetDescription() const override
bool InitializeControls(LadspaEffectSettings &settings) const
std::unique_ptr< EffectOutputs > MakeOutputs() const override
Produce an object to hold values to send to effect output meters.
OptionalMessage ImportPresets(EffectSettings &settings) override
unsigned mAudioIns
Definition: LadspaEffect.h:165
const int mIndex
Definition: LadspaEffect.h:154
std::shared_ptr< EffectInstance > MakeInstance() const override
Make an object maintaining short-term state of an Effect.
bool IsDefault() const override
Whether the effect sorts "above the line" in the menus.
bool FullyInitializePlugin()
PluginPath GetPath() const override
EffectSettings MakeSettings() const override
ArrayOf< unsigned long > mInputPorts
Definition: LadspaEffect.h:167
VendorSymbol GetVendor() const override
int ShowClientInterface(wxWindow &parent, wxDialog &dialog, EffectUIValidator *pValidator, bool forceModal) override
int mNumOutputControls
Definition: LadspaEffect.h:174
virtual ~LadspaEffect()
const float & mVal
void OnErase(wxEraseEvent &evt)
void OnSize(wxSizeEvent &evt)
LadspaEffectMeter(wxWindow *parent, const float &val, float min, float max)
void OnIdle(wxIdleEvent &evt)
virtual ~LadspaEffectMeter()
void OnPaint(wxPaintEvent &evt)
LadspaEffectOptionsDialog(wxWindow *parent, EffectDefinitionInterface &effect, bool &var)
void PopulateOrExchange(ShuttleGui &S)
void OnOk(wxCommandEvent &evt)
EffectDefinitionInterface & mEffect
PluginPaths FindModulePaths(PluginManagerInterface &pm) override
virtual ~LadspaEffectsModule()
ComponentInterfaceSymbol GetSymbol() const override
std::unique_ptr< ComponentInterface > LoadPlugin(const PluginPath &path) override
Load the plug-in at a path reported by DiscoverPluginsAtPath.
FilePath InstallPath() override
Where plug-in files should be copied to install them.
TranslatableString GetDescription() const override
EffectFamilySymbol GetOptionalFamilySymbol() override
A symbol identifying the family of plug-ins provided by this.
unsigned DiscoverPluginsAtPath(const PluginPath &path, TranslatableString &errMsg, const RegistrationCallback &callback) override
FilePaths GetSearchPaths()
const FileExtensions & GetFileExtensions() override
File types associated with this protocol.
VendorSymbol GetVendor() const override
void Terminate() override
Called just prior to deletion to allow releasing any resources.
wxString GetVersion() const override
bool Initialize() override
Called immediately after creation. Let provider initialize.
PluginPath GetPath() const override
bool CheckPluginExist(const PluginPath &path) const override
Performs plugin/module existence check, still plugin may fail to load. Implementation should avoid lo...
void AutoRegisterPlugins(PluginManagerInterface &pm) override
Called so that a provider of a static set of plug-ins can register them.
Instance(const PerTrackEffect &processor)
const PerTrackEffect & mProcessor
virtual void FindFilesInPathList(const wxString &pattern, const FilePaths &pathList, FilePaths &files, bool directories=false)=0
virtual bool IsPluginRegistered(const PluginPath &path, const TranslatableString *pName=nullptr)=0
Was the plugin registry already populated for a path (maybe from loading the config file)?
static const PluginID & DefaultRegistrationCallback(PluginProvider *provider, ComponentInterface *ident)
std::function< const PluginID &(PluginProvider *, ComponentInterface *) > RegistrationCallback
Further expand a path reported by FindModulePaths.
Derived from ShuttleGuiBase, an Audacity specific class for shuttling data to and from GUI.
Definition: ShuttleGui.h:628
Holds a msgid for the translation catalog; may also bind format arguments.
An alternative to using wxWindowAccessible, which in wxWidgets 3.1.1 contained GetParent() which was ...
Extend wxArrayString with move operations and construction and insertion fromstd::initializer_list.
#define LADSPA_HINT_DEFAULT_100
Definition: ladspa.h:303
#define LADSPA_IS_HINT_BOUNDED_BELOW(x)
Definition: ladspa.h:310
#define LADSPA_HINT_DEFAULT_MAXIMUM
Definition: ladspa.h:290
#define LADSPA_HINT_DEFAULT_440
Definition: ladspa.h:308
#define LADSPA_HINT_DEFAULT_LOW
Definition: ladspa.h:272
#define LADSPA_IS_HINT_BOUNDED_ABOVE(x)
Definition: ladspa.h:311
#define LADSPA_HINT_DEFAULT_0
Definition: ladspa.h:295
#define LADSPA_IS_HINT_INTEGER(x)
Definition: ladspa.h:315
#define LADSPA_IS_HINT_LOGARITHMIC(x)
Definition: ladspa.h:314
#define LADSPA_HINT_DEFAULT_HIGH
Definition: ladspa.h:286
#define LADSPA_IS_HINT_TOGGLED(x)
Definition: ladspa.h:312
#define LADSPA_HINT_DEFAULT_MASK
Definition: ladspa.h:258
#define LADSPA_HINT_DEFAULT_MIDDLE
Definition: ladspa.h:279
int LADSPA_PortDescriptor
Definition: ladspa.h:152
#define LADSPA_HINT_DEFAULT_1
Definition: ladspa.h:300
float LADSPA_Data
Definition: ladspa.h:84
#define LADSPA_HINT_DEFAULT_MINIMUM
Definition: ladspa.h:265
const LADSPA_Descriptor *(* LADSPA_Descriptor_Function)(unsigned long Index)
Definition: ladspa.h:593
#define LADSPA_IS_PORT_INPUT(x)
Definition: ladspa.h:168
#define LADSPA_IS_PORT_AUDIO(x)
Definition: ladspa.h:171
void * LADSPA_Handle
Definition: ladspa.h:363
#define LADSPA_IS_PORT_CONTROL(x)
Definition: ladspa.h:170
#define LADSPA_IS_PORT_OUTPUT(x)
Definition: ladspa.h:169
#define LADSPA_HINT_DEFAULT_NONE
Definition: ladspa.h:261
#define LADSPA_IS_HINT_SAMPLE_RATE(x)
Definition: ladspa.h:313
Services * Get()
Fetch the global instance, or nullptr if none is yet installed.
Definition: BasicUI.cpp:194
FILES_API FilePath PlugInDir()
The user plug-in directory (not a system one)
bool SetConfig(const EffectDefinitionInterface &ident, ConfigurationType type, const RegistryPath &group, const RegistryPath &key, const Value &value)
bool GetConfig(const EffectDefinitionInterface &ident, ConfigurationType type, const RegistryPath &group, const RegistryPath &key, Value &var, const Value &defval)
float InputControlPortDefaultValue(const LADSPA_PortRangeHint &hint, double sampleRate)
std::pair< float, float > InputControlPortBounds(const LADSPA_PortRangeHint &hint, double sampleRate)
float ClampInputControlValue(const LADSPA_PortRangeHint &hint, float val, float lower, float upper)
std::vector< CommandFlagOptions > & Options()
Definition: Menus.cpp:535
STL namespace.
_LADSPA_Descriptor is a structure that provides the API to a LADSPA (Linux Audio Plugin Architecture)...
Definition: ladspa.h:373
LADSPA_Handle(* instantiate)(const struct _LADSPA_Descriptor *Descriptor, unsigned long SampleRate)
Definition: ladspa.h:437
void(* deactivate)(LADSPA_Handle Instance)
Definition: ladspa.h:549
void(* cleanup)(LADSPA_Handle Instance)
Definition: ladspa.h:558
const char *const * PortNames
Definition: ladspa.h:415
void(* activate)(LADSPA_Handle Instance)
Definition: ladspa.h:489
const char * Copyright
Definition: ladspa.h:402
void(* connect_port)(LADSPA_Handle Instance, unsigned long Port, LADSPA_Data *DataLocation)
Definition: ladspa.h:466
const LADSPA_PortDescriptor * PortDescriptors
Definition: ladspa.h:410
unsigned long PortCount
Definition: ladspa.h:406
const char * Name
Definition: ladspa.h:393
const char * Maker
Definition: ladspa.h:397
const LADSPA_PortRangeHint * PortRangeHints
Definition: ladspa.h:419
_LADSPA_PortRangeHint is a structure that gives parameter validation information for a LADSPA (Linux ...
Definition: ladspa.h:337
LADSPA_PortRangeHintDescriptor HintDescriptor
Definition: ladspa.h:340
LADSPA_Data LowerBound
Definition: ladspa.h:345
LADSPA_Data UpperBound
Definition: ladspa.h:350
Externalized state of a plug-in.
bool RealtimeAddProcessor(EffectSettings &settings, EffectOutputs *pOutputs, unsigned numChannels, float sampleRate) override
size_t ProcessBlock(EffectSettings &settings, const float *const *inBlock, float *const *outBlock, size_t blockLen) override
Called for destructive effect computation.
size_t RealtimeProcess(size_t group, EffectSettings &settings, const float *const *inBuf, float *const *outBuf, size_t numSamples) override
bool RealtimeResume() override
bool RealtimeProcessEnd(EffectSettings &settings) noexcept override
settings can be updated to let a dialog change appearance at idle
bool RealtimeInitialize(EffectSettings &settings, double sampleRate) override
SampleCount GetLatency(const EffectSettings &settings, double sampleRate) const override
bool ProcessFinalize() noexcept override
bool RealtimeSuspend() override
std::vector< LADSPA_Handle > mSlaves
unsigned GetAudioInCount() const override
How many input buffers to allocate at once.
const LadspaEffect & GetEffect() const
bool RealtimeFinalize(EffectSettings &settings) noexcept override
bool ProcessInitialize(EffectSettings &settings, double sampleRate, ChannelNames chanMap) override
bool RealtimeProcessStart(MessagePackage &package) override
settings are possibly changed, since last call, by an asynchronous dialog
unsigned GetAudioOutCount() const override
How many output buffers to allocate at once.
bool UpdateUI() override
Update appearance of the panel for changes in settings.
void OnSlider(wxCommandEvent &evt)
void Disconnect() override
On the first call only, may disconnect from further event handling.
ArrayOf< wxTextCtrl * > mFields
bool ValidateUI() override
Get settings data from the panel; may make error dialogs and return false.
ArrayOf< wxStaticText * > mLabels
const LadspaEffectOutputs *const mpOutputs
ArrayOf< wxSlider * > mSliders
wxWeakRef< wxDialog > mDialog
void PopulateUI(ShuttleGui &S)
void UpdateControl(int index, float value, float epsilon)
void OnCheckBox(wxCommandEvent &evt)
ArrayOf< wxCheckBox * > mToggles
Validator(EffectUIClientInterface &effect, EffectSettingsAccess &access, double sampleRate, EffectType type, const LadspaEffectOutputs *pOutputs)
void UpdateControls(const LadspaEffectSettings &src)
LadspaEffectSettings mSettings
const LadspaEffect & GetEffect()
void OnTextCtrl(wxCommandEvent &evt)
NumericTextCtrl * mDuration
std::vector< LadspaEffectMeter * > mMeters
const EffectType mType
Carry output control port information back to main thread.
Definition: LadspaEffect.h:54
~LadspaEffectOutputs() override
void Assign(EffectOutputs &&src) override
Update one Outputs object from another.
std::vector< float > controls
Definition: LadspaEffect.h:62
std::unique_ptr< EffectOutputs > Clone() const override
std::vector< float > controls
Definition: LadspaEffect.h:50
Options & AutoPos(bool enable)