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