Audacity 3.2.0
PluginManager.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 PluginManager.cpp
6
7 Leland Lucius
8
9*******************************************************************/
22#include "PluginManager.h"
23
24
25
26#include <algorithm>
27
28#include <wx/log.h>
29#include <wx/tokenzr.h>
30
31#include "BasicUI.h"
32#include "PluginProvider.h"
33
34#include "Internat.h" // for macro XO
35#include "FileNames.h"
36#include "MemoryX.h"
37#include "ModuleManager.h"
39#include "Base64.h"
40#include "Variant.h"
41
43//
44// PluginManager
45//
47
48// Registry has the list of plug ins
49#define REGVERKEY wxString(wxT("/pluginregistryversion"))
50#define REGROOT wxString(wxT("/pluginregistry/"))
51#define REGCUSTOMPATHS wxString(wxT("/providercustompaths"))
52
53// Settings has the values of the plug in settings.
54#define SETVERKEY wxString(wxT("/pluginsettingsversion"))
55#define SETVERCUR wxString(wxT("1.0"))
56#define SETROOT wxString(wxT("/pluginsettings/"))
57
58#define KEY_ID wxT("ID")
59#define KEY_PATH wxT("Path")
60#define KEY_SYMBOL wxT("Symbol")
61#define KEY_NAME wxT("Name")
62#define KEY_VENDOR wxT("Vendor")
63#define KEY_VERSION wxT("Version")
64#define KEY_DESCRIPTION wxT("Description")
65#define KEY_LASTUPDATED wxT("LastUpdated")
66#define KEY_ENABLED wxT("Enabled")
67#define KEY_VALID wxT("Valid")
68#define KEY_PROVIDERID wxT("ProviderID")
69#define KEY_EFFECTTYPE wxT("EffectType")
70#define KEY_EFFECTFAMILY wxT("EffectFamily")
71#define KEY_EFFECTDEFAULT wxT("EffectDefault")
72#define KEY_EFFECTINTERACTIVE wxT("EffectInteractive")
73#define KEY_EFFECTREALTIME wxT("EffectRealtime")
74#define KEY_EFFECTAUTOMATABLE wxT("EffectAutomatable")
75#define KEY_EFFECTTYPE_NONE wxT("None")
76#define KEY_EFFECTTYPE_ANALYZE wxT("Analyze")
77#define KEY_EFFECTTYPE_GENERATE wxT("Generate")
78#define KEY_EFFECTTYPE_PROCESS wxT("Process")
79#define KEY_EFFECTTYPE_TOOL wxT("Tool")
80#define KEY_EFFECTTYPE_HIDDEN wxT("Hidden")
81#define KEY_IMPORTERIDENT wxT("ImporterIdent")
82//#define KEY_IMPORTERFILTER wxT("ImporterFilter")
83#define KEY_IMPORTEREXTENSIONS wxT("ImporterExtensions")
84
85// ============================================================================
86//
87// PluginManagerInterface implementation
88//
89// ============================================================================
90
92 PluginProvider *provider, ComponentInterface *pInterface )
93{
94 if(auto effectDefinitionInterface = dynamic_cast<EffectDefinitionInterface*>(pInterface))
95 return PluginManager::Get().RegisterPlugin(provider, effectDefinitionInterface, PluginTypeEffect);
96 return PluginManager::Get().RegisterPlugin(provider, pInterface);
97}
98
100 PluginProvider *provider, ComponentInterface *pInterface )
101{
102 return PluginManager::Get().RegisterPlugin(provider, pInterface);
103}
104
106{
107 auto pPlugin = GetPlugin( ID );
108 if ( pPlugin )
109 return GetPluginEnabledSetting( *pPlugin );
110 return {};
111}
112
114 const PluginDescriptor &desc ) const
115{
116 switch ( desc.GetPluginType() ) {
117 case PluginTypeModule: {
118 // Retrieve optional family symbol that was recorded in
119 // RegisterPlugin() for the module
120 auto family = desc.GetEffectFamily();
121 if ( family.empty() ) // as for built-in effect and command modules
122 return {};
123 else
124 return wxT('/') + family + wxT("/Enable");
125 }
126 case PluginTypeEffect:
127 // do NOT use GetEffectFamily() for this descriptor, but instead,
128 // delegate to the plugin descriptor of the provider, which may
129 // be different (may be empty)
130 return GetPluginEnabledSetting( desc.GetProviderID() );
131 default:
132 return {};
133 }
134}
135
137 const PluginPath &path, const TranslatableString *pName)
138{
139 for (auto &pair : mRegisteredPlugins) {
140 if (auto &descriptor = pair.second; descriptor.GetPath() == path) {
141 if (pName)
142 descriptor.SetSymbol(
143 { descriptor.GetSymbol().Internal(), *pName });
144 return true;
145 }
146 }
147 return false;
148}
149
150bool PluginManager::IsPluginLoaded(const wxString& ID) const
151{
152 return mLoadedInterfaces.find(ID) != mLoadedInterfaces.end();
153}
154
156{
157 mRegisteredPlugins[desc.GetID()] = std::move(desc);
158}
159
161{
162 PluginDescriptor & plug =
163 CreatePlugin(GetID(provider), provider, PluginTypeModule);
165
166 plug.SetEnabled(true);
167 plug.SetValid(true);
168
169 return plug.GetID();
170}
171
173 PluginProvider *provider, ComponentInterface *command)
174{
176
177 plug.SetProviderID(PluginManager::GetID(provider));
178
179 plug.SetEnabled(true);
180 plug.SetValid(true);
181
182 return plug.GetID();
183}
184
186 PluginProvider *provider, EffectDefinitionInterface *effect, int type)
187{
188 PluginDescriptor & plug = CreatePlugin(GetID(effect), effect, (PluginType)type);
189
190 plug.SetProviderID(PluginManager::GetID(provider));
191
192 plug.SetEffectType(effect->GetClassification());
193 plug.SetEffectFamily(effect->GetFamily().Internal());
194 plug.SetEffectInteractive(effect->IsInteractive());
195 plug.SetEffectDefault(effect->IsDefault());
196 plug.SetRealtimeSupport(effect->RealtimeSupport());
198
199 plug.SetEnabled(true);
200 plug.SetValid(true);
201
202 return plug.GetID();
203}
204
205void PluginManager::FindFilesInPathList(const wxString & pattern,
206 const FilePaths & pathList,
207 FilePaths & files,
208 bool directories)
209{
210
211 wxLogNull nolog;
212
213 // Why bother...
214 if (pattern.empty())
215 {
216 return;
217 }
218
219 // TODO: We REALLY need to figure out the "Audacity" plug-in path(s)
220
221 FilePaths paths;
222
223 // Add the "per-user" plug-ins directory
224 {
225 const wxFileName &ff = FileNames::PlugInDir();
226 paths.push_back(ff.GetFullPath());
227 }
228
229 // Add the "Audacity" plug-ins directory
231#if defined(__WXMAC__)
232 // Path ends for example in "Audacity.app/Contents/MacOSX"
233 //ff.RemoveLastDir();
234 //ff.RemoveLastDir();
235 // just remove the MacOSX part.
236 ff.RemoveLastDir();
237#endif
238 ff.AppendDir(wxT("plug-ins"));
239 paths.push_back(ff.GetPath());
240
241 // Weed out duplicates
242 for (const auto &filePath : pathList)
243 {
244 ff = filePath;
245 const wxString path{ ff.GetFullPath() };
246 if (paths.Index(path, wxFileName::IsCaseSensitive()) == wxNOT_FOUND)
247 {
248 paths.push_back(path);
249 }
250 }
251
252 // Find all matching files in each path
253 for (size_t i = 0, cnt = paths.size(); i < cnt; i++)
254 {
255 ff = paths[i] + wxFILE_SEP_PATH + pattern;
256 wxDir::GetAllFiles(ff.GetPath(), &files, ff.GetFullName(), directories ? wxDIR_DEFAULT : wxDIR_FILES);
257 }
258
259 return;
260}
261
263 const RegistryPath & group)
264{
265 return HasGroup(Group(type, ID, group));
266}
267
269 const PluginID & ID, const RegistryPath & group, RegistryPaths & subgroups)
270{
271 return GetSubgroups(Group(type, ID, group), subgroups);
272}
273
275 const RegistryPath & group, const RegistryPath & key)
276{
277 return HasConfigValue(Key(type, ID, group, key));
278}
279
281 const RegistryPath & group, const RegistryPath & key,
283{
284 return GetConfigValue(Key(type, ID, group, key), var, defval);
285}
286
288 const RegistryPath & group, const RegistryPath & key,
290{
291 return SetConfigValue(Key(type, ID, group, key), value);
292}
293
295 const PluginID & ID, const RegistryPath & group)
296{
297 bool result = GetSettings()->DeleteGroup(Group(type, ID, group));
298 if (result)
299 {
300 GetSettings()->Flush();
301 }
302
303 return result;
304}
305
307 const RegistryPath & group, const RegistryPath & key)
308{
309 bool result = GetSettings()->DeleteEntry(Key(type, ID, group, key));
310 if (result)
311 {
312 GetSettings()->Flush();
313 }
314
315 return result;
316}
317
318// ============================================================================
319//
320// PluginManager
321//
322// ============================================================================
323
324// The one and only PluginManager
325std::unique_ptr<PluginManager> PluginManager::mInstance{};
326
327// ----------------------------------------------------------------------------
328// Creation/Destruction
329// ----------------------------------------------------------------------------
330
332{
333 mSettings = NULL;
334}
335
337{
338 // Ensure termination (harmless if already done)
339 Terminate();
340}
341
343{
344 ModuleManager & moduleManager = ModuleManager::Get();
345 //ModuleManager::DiscoverProviders was called earlier, so we
346 //can be sure that providers are already loaded
347
348 //Check all known plugins to ensure they are still valid.
349 for (auto it = mRegisteredPlugins.begin(); it != mRegisteredPlugins.end();) {
350 auto &pluginDesc = it->second;
351 const auto pluginType = pluginDesc.GetPluginType();
352 if(pluginType == PluginTypeNone || pluginType == PluginTypeModule)
353 {
354 ++it;
355 continue;
356 }
357
358 if(!moduleManager.CheckPluginExist(pluginDesc.GetProviderID(), pluginDesc.GetPath()))
359 it = mRegisteredPlugins.erase(it);
360 else
361 ++it;
362 }
363
364 Save();
365}
366
367// ----------------------------------------------------------------------------
368// PluginManager implementation
369// ----------------------------------------------------------------------------
370
372
373// ============================================================================
374//
375// Return reference to singleton
376//
377// (Thread-safe...no active threading during construction or after destruction)
378// ============================================================================
379
381{
382 if (!mInstance)
383 {
385 }
386
387 return *mInstance;
388}
389
391{
392 sFactory = move(factory);
393
394 // Always load the registry first
395 Load();
396
397 // And force load of setting to verify it's accessible
398 GetSettings();
399
400 auto &mm = ModuleManager::Get();
401 mm.DiscoverProviders();
402 for (auto& [id, module] : mm.Providers()) {
403 RegisterPlugin(module.get());
404 // Allow the module to auto-register children
405 module->AutoRegisterPlugins(*this);
406 }
407
409}
410
412{
413 // Get rid of all non-module(effects?) plugins first
414 for(auto& p : mRegisteredPlugins)
415 {
416 auto& desc = p.second;
417 if(desc.GetPluginType() == PluginTypeEffect)
418 mLoadedInterfaces.erase(desc.GetID());
419 }
420
421 // Now get rid of others
422 mRegisteredPlugins.clear();
423 mLoadedInterfaces.clear();
424}
425
426bool PluginManager::DropFile(const wxString &fileName)
427{
428 using namespace BasicUI;
429 auto &mm = ModuleManager::Get();
430 const wxFileName src{ fileName };
431
432 for (auto &plug : PluginsOfType(PluginTypeModule)) {
433 auto module = static_cast<PluginProvider *>
434 (mm.CreateProviderInstance(plug.GetID(), plug.GetPath()));
435 if (! module)
436 continue;
437
438 const auto &ff = module->InstallPath();
439 const auto &extensions = module->GetFileExtensions();
440 if ( !ff.empty() &&
441 extensions.Index(src.GetExt(), false) != wxNOT_FOUND ) {
442 TranslatableString errMsg;
443 // Do dry-run test of the file format
444 unsigned nPlugIns =
445 module->DiscoverPluginsAtPath(fileName, errMsg, {});
446 if (nPlugIns) {
447 // File contents are good for this module, so check no others.
448 // All branches of this block return true, even in case of
449 // failure for other reasons, to signal that other drag-and-drop
450 // actions should not be tried.
451
452 // Find path to copy it
453 wxFileName dst;
454 dst.AssignDir( ff );
455 dst.SetFullName( src.GetFullName() );
456 if ( dst.Exists() ) {
457 // Query whether to overwrite
458 bool overwrite = (MessageBoxResult::Yes == ShowMessageBox(
459 XO("Overwrite the plug-in file %s?")
460 .Format( dst.GetFullPath() ),
462 .Caption(XO("Plug-in already exists"))
463 .ButtonStyle(Button::YesNo)));
464 if ( !overwrite )
465 return true;
466 }
467
468 // Move the file or subtree
469 bool copied = false;
470 auto dstPath = dst.GetFullPath();
471 if ( src.FileExists() )
472 // A simple one-file plug-in
473 copied = FileNames::DoCopyFile(
474 src.GetFullPath(), dstPath, true );
475 else {
476 // A sub-folder
477 // such as for some VST packages
478 // Recursive copy needed -- to do
479 return true;
480 }
481
482 if (!copied) {
484 XO("Plug-in file is in use. Failed to overwrite") );
485 return true;
486 }
487
488 // Register for real
489 std::vector<PluginID> ids;
490 std::vector<wxString> names;
491 nPlugIns = module->DiscoverPluginsAtPath(dstPath, errMsg,
493 -> const PluginID& {
494 // Register as by default, but also collecting the PluginIDs
495 // and names
497 provider, ident);
498 ids.push_back(id);
499 names.push_back( ident->GetSymbol().Translation() );
500 return id;
501 });
502 if ( ! nPlugIns ) {
503 // Unlikely after the dry run succeeded
505 XO("Failed to register:\n%s").Format( errMsg ) );
506 return true;
507 }
508
509 // Ask whether to enable the plug-ins
510 if (auto nIds = ids.size()) {
511 auto message = XPC(
512 /* i18n-hint A plug-in is an optional added program for a sound
513 effect, or generator, or analyzer */
514 "Enable this plug-in?\n",
515 "Enable these plug-ins?\n",
516 0,
517 "plug-ins"
518 )( nIds );
519 for (const auto &name : names)
520 message.Join( Verbatim( name ), wxT("\n") );
521 bool enable = (MessageBoxResult::Yes == ShowMessageBox(
522 message,
524 .Caption(XO("Enable new plug-ins"))
525 .ButtonStyle(Button::YesNo)));
526 for (const auto &id : ids)
527 mRegisteredPlugins[id].SetEnabled(enable);
528 // Make changes to enabled status persist:
529 this->Save();
530 this->NotifyPluginsChanged();
531 }
532
533 return true;
534 }
535 }
536 }
537
538 return false;
539}
540
542{
543 // Create/Open the registry
544 auto pRegistry = sFactory(FileNames::PluginRegistry());
545 auto &registry = *pRegistry;
546
547 // If this group doesn't exist then we have something that's not a registry.
548 // We should probably warn the user, but it's pretty unlikely that this will happen.
549 if (!registry.HasGroup(REGROOT))
550 {
551 // Must start over
552 // This DeleteAll affects pluginregistry.cfg only, not audacity.cfg
553 // That is, the memory of on/off states of effect (and generator,
554 // analyzer, and tool) plug-ins
555 registry.Clear();
556 registry.Flush();
557 return;
558 }
559
560 // Check for a registry version that we can understand
561 // TODO: Should also check for a registry file that is newer than
562 // what we can understand.
563 mRegver = registry.Read(REGVERKEY);
564 if (Regver_lt(mRegver, "1.1")) {
565 // Conversion code here, for when registry version changes.
566
567 // We iterate through the effects, possibly updating their info.
568 wxString group = GetPluginTypeString(PluginTypeEffect);
569 wxString cfgPath = REGROOT + group + wxCONFIG_PATH_SEPARATOR;
570 wxArrayString groupsToDelete;
571
572 auto cfgGroup = registry.BeginGroup(cfgPath);
573 for(const auto& groupName : registry.GetChildGroups())
574 {
575 auto effectGroup = registry.BeginGroup(groupName);
576 wxString effectSymbol = registry.Read(KEY_SYMBOL, "");
577 wxString effectVersion = registry.Read(KEY_VERSION, "");
578
579
580 // For 2.3.0 the plugins we distribute have moved around.
581 // So we upped the registry version number to 1.1.
582 // These particular config edits were originally written to fix Bug 1914.
583 if (Regver_le(mRegver, "1.0")) {
584 // Nyquist prompt is a built-in that has moved to the tools menu.
585 if (effectSymbol == NYQUIST_PROMPT_ID) {
586 registry.Write(KEY_EFFECTTYPE, "Tool");
587 // Old version of SDE was in Analyze menu. Now it is in Tools.
588 // We don't want both the old and the new.
589 } else if ((effectSymbol == "Sample Data Export") && (effectVersion == "n/a")) {
590 groupsToDelete.push_back(cfgPath + groupName);
591 // Old version of SDI was in Generate menu. Now it is in Tools.
592 } else if ((effectSymbol == "Sample Data Import") && (effectVersion == "n/a")) {
593 groupsToDelete.push_back(cfgPath + groupName);
594 }
595 }
596 }
597 // Doing the deletion within the search loop risked skipping some items,
598 // hence the delayed delete.
599 for (unsigned int i = 0; i < groupsToDelete.size(); i++) {
600 registry.DeleteGroup(groupsToDelete[i]);
601 }
602 // Updates done. Make sure we read the updated data later.
603 registry.Flush();
604 }
605
606 // Load all provider plugins first
607 LoadGroup(&registry, PluginTypeModule);
608
609 // Now the rest
610 LoadGroup(&registry, PluginTypeEffect);
612 LoadGroup(&registry, PluginTypeExporter);
613 LoadGroup(&registry, PluginTypeImporter);
614
615 LoadGroup(&registry, PluginTypeStub);
616 return;
617}
618
620{
621#ifdef __WXMAC__
622 // Bug 1590: On Mac, we should purge the registry of Nyquist plug-ins
623 // bundled with other versions of Audacity, assuming both versions
624 // were properly installed in /Applications (or whatever it is called in
625 // your locale)
626
627 const auto fullExePath = PlatformCompatibility::GetExecutablePath();
628
629 // Strip rightmost path components up to *.app
630 wxFileName exeFn{ fullExePath };
631 exeFn.SetEmptyExt();
632 exeFn.SetName(wxString{});
633 while(exeFn.GetDirCount() && !exeFn.GetDirs().back().EndsWith(".app"))
634 exeFn.RemoveLastDir();
635
636 const auto goodPath = exeFn.GetPath();
637
638 if(exeFn.GetDirCount())
639 exeFn.RemoveLastDir();
640 const auto possiblyBadPath = exeFn.GetPath();
641
642 auto AcceptPath = [&](const wxString &path) {
643 if (!path.StartsWith(possiblyBadPath))
644 // Assume it's not under /Applications
645 return true;
646 if (path.StartsWith(goodPath))
647 // It's bundled with this executable
648 return true;
649 return false;
650 };
651#else
652 auto AcceptPath = [](const wxString&){ return true; };
653#endif
654
655 wxString strVal;
656 bool boolVal;
657 wxString cfgPath = REGROOT + GetPluginTypeString(type) + wxCONFIG_PATH_SEPARATOR;
658
659 const auto cfgGroup = pRegistry->BeginGroup(cfgPath);
660 for(const auto& group : pRegistry->GetChildGroups())
661 {
662 PluginDescriptor plug;
663 const auto effectGroup = pRegistry->BeginGroup(group);
664
665 auto groupName = ConvertID(group);
666
667 // Bypass group if the ID is already in use
668 if (mRegisteredPlugins.count(groupName))
669 continue;
670
671 // Set the ID and type
672 plug.SetID(groupName);
673 plug.SetPluginType(type);
674
675 // Get the provider ID and bypass group if not found
676 if (!pRegistry->Read(KEY_PROVIDERID, &strVal, {}))
677 {
678 // Bypass group if the provider isn't valid
679 if (!strVal.empty() && !mRegisteredPlugins.count(strVal))
680 continue;
681 }
682 plug.SetProviderID(PluginID(strVal));
683
684 // Get the path (optional)
685 pRegistry->Read(KEY_PATH, &strVal, {});
686 if (!AcceptPath(strVal))
687 // Ignore the obsolete path in the config file, during session,
688 // but don't remove it from the file. Maybe you really want to
689 // switch back to the other version of Audacity and lose nothing.
690 continue;
691 plug.SetPath(strVal);
692
693 /*
694 // PRL: Ignore names written in configs before 2.3.0!
695 // use Internal string only! Let the present version of Audacity map
696 // that to a user-visible string.
697 // Get the name and bypass group if not found
698 if (!pRegistry->Read(KEY_NAME, &strVal))
699 {
700 continue;
701 }
702 plug.SetName(strVal);
703 */
704
705 // Get the symbol...Audacity 2.3.0 or later requires it
706 // bypass group if not found
707 // Note, KEY_SYMBOL started getting written to config files in 2.1.0.
708 // KEY_NAME (now ignored) was written before that, but only for VST
709 // effects.
710 if (!pRegistry->Read(KEY_SYMBOL, &strVal))
711 continue;
712
713 // Related to Bug2778: config file only remembered an internal name,
714 // so this symbol may not contain the correct TranslatableString.
715 // See calls to IsPluginRegistered which can correct that.
716 plug.SetSymbol(strVal);
717
718 // Get the version and bypass group if not found
719 if (!pRegistry->Read(KEY_VERSION, &strVal))
720 {
721 continue;
722 }
723 plug.SetVersion(strVal);
724
725 // Get the vendor and bypass group if not found
726 if (!pRegistry->Read(KEY_VENDOR, &strVal))
727 {
728 continue;
729 }
730 plug.SetVendor( strVal );
731
732#if 0
733 // This was done before version 2.2.2, but the value was not really used
734 // But absence of a value will cause early versions to skip the group
735 // Therefore we still write a blank to keep pluginregistry.cfg
736 // backwards-compatible
737
738 // Get the description and bypass group if not found
739 if (!pRegistry->Read(KEY_DESCRIPTION, &strVal))
740 {
741 continue;
742 }
743#endif
744
745 // Is it enabled...default to no if not found
746 pRegistry->Read(KEY_ENABLED, &boolVal, false);
747 plug.SetEnabled(boolVal);
748
749 // Is it valid...default to no if not found
750 pRegistry->Read(KEY_VALID, &boolVal, false);
751 plug.SetValid(boolVal);
752
753 switch (type)
754 {
755 case PluginTypeModule:
756 {
757 // Nothing to do here yet
758 }
759 break;
760
761 case PluginTypeEffect:
762 {
763 // Get the effect type and bypass group if not found
764 if (!pRegistry->Read(KEY_EFFECTTYPE, &strVal))
765 continue;
766
767 if (strVal == KEY_EFFECTTYPE_NONE)
769 else if (strVal == KEY_EFFECTTYPE_ANALYZE)
771 else if (strVal == KEY_EFFECTTYPE_GENERATE)
773 else if (strVal == KEY_EFFECTTYPE_PROCESS)
775 else if (strVal == KEY_EFFECTTYPE_TOOL)
777 else if (strVal == KEY_EFFECTTYPE_HIDDEN)
779 else
780 continue;
781
782 // Get the effect family and bypass group if not found
783 if (!pRegistry->Read(KEY_EFFECTFAMILY, &strVal))
784 {
785 continue;
786 }
787 plug.SetEffectFamily(strVal);
788
789 // Is it a default (above the line) effect and bypass group if not found
790 if (!pRegistry->Read(KEY_EFFECTDEFAULT, &boolVal))
791 {
792 continue;
793 }
794 plug.SetEffectDefault(boolVal);
795
796 // Is it an interactive effect and bypass group if not found
797 if (!pRegistry->Read(KEY_EFFECTINTERACTIVE, &boolVal))
798 {
799 continue;
800 }
801 plug.SetEffectInteractive(boolVal);
802
803 // Is it a realtime capable effect and bypass group if not found
804 if (!pRegistry->Read(KEY_EFFECTREALTIME, &strVal))
805 {
806 continue;
807 }
808 plug.DeserializeRealtimeSupport(strVal);
809
810 // Does the effect support automation...bypass group if not found
811 if (!pRegistry->Read(KEY_EFFECTAUTOMATABLE, &boolVal))
812 {
813 continue;
814 }
815 plug.SetEffectAutomatable(boolVal);
816 }
817 break;
818
820 {
821 // Get the importer identifier and bypass group if not found
822 if (!pRegistry->Read(KEY_IMPORTERIDENT, &strVal))
823 {
824 continue;
825 }
826 plug.SetImporterIdentifier(strVal);
827
828 // Get the importer extensions and bypass group if not found
829 if (!pRegistry->Read(KEY_IMPORTEREXTENSIONS, &strVal))
830 {
831 continue;
832 }
833 FileExtensions extensions;
834 wxStringTokenizer tkr(strVal, wxT(":"));
835 while (tkr.HasMoreTokens())
836 {
837 extensions.push_back(tkr.GetNextToken());
838 }
839 plug.SetImporterExtensions(extensions);
840 }
841 break;
842
843 case PluginTypeStub:
844 {
845 // Nothing additional for stubs
846 }
847 break;
848
849 // Not used by 2.1.1 or greater and should be removed after a few releases past 2.1.0.
850 case PluginTypeNone:
851 {
852 // Used for stub groups
853 }
854 break;
855
856 default:
857 {
858 continue;
859 }
860 }
861
862 // Everything checked out...accept the plugin
863 mRegisteredPlugins[groupName] = std::move(plug);
864 }
865
866 return;
867}
868
870{
871 // Create/Open the registry
872 auto pRegistry = sFactory(FileNames::PluginRegistry());
873 auto &registry = *pRegistry;
874
875 // Clear pluginregistry.cfg (not audacity.cfg)
876 registry.Clear();
877
878 // Save the individual groups
879 SaveGroup(&registry, PluginTypeEffect);
880 SaveGroup(&registry, PluginTypeExporter);
882 SaveGroup(&registry, PluginTypeImporter);
883 SaveGroup(&registry, PluginTypeStub);
884
885 // Not used by 2.1.1 or greater, but must save to allow users to switch between 2.1.0
886 // and 2.1.1+. This should be removed after a few releases past 2.1.0.
887 //SaveGroup(&registry, PluginTypeNone);
888
889 // And now the providers
890 SaveGroup(&registry, PluginTypeModule);
891
892 // Write the version string
893 registry.Write(REGVERKEY, REGVERCUR);
894
895 // Just to be safe
896 registry.Flush();
897
899}
900
902{
904}
905
907{
908 return mRegver;
909}
910
911
913{
914 auto group = mSettings->BeginGroup(REGCUSTOMPATHS);
915 const auto key = GetID(&provider);
916 const auto paths = mSettings->Read(key, wxString{});
917 const auto wxarr = wxSplit(paths, ';');
918 return PluginPaths(wxarr.begin(), wxarr.end());
919}
920
922{
923 auto group = mSettings->BeginGroup(REGCUSTOMPATHS);
924 const auto key = GetID(&provider);
925 wxArrayString wxarr;
926 std::copy(paths.begin(), paths.end(), std::back_inserter(wxarr));
927 mSettings->Write(key, wxJoin(wxarr, ';'));
928}
929
931{
932 wxString group = GetPluginTypeString(type);
933 for (auto &pair : mRegisteredPlugins) {
934 auto & plug = pair.second;
935
936 if (plug.GetPluginType() != type)
937 {
938 continue;
939 }
940
941 const auto pluginGroup = pRegistry->BeginGroup(REGROOT + group + wxCONFIG_PATH_SEPARATOR + ConvertID(plug.GetID()));
942
943 pRegistry->Write(KEY_PATH, plug.GetPath());
944
945 // See comments with the corresponding load-time call to SetSymbol().
946 pRegistry->Write(KEY_SYMBOL, plug.GetSymbol().Internal());
947
948 // PRL: Writing KEY_NAME which is no longer read, but older Audacity
949 // versions expect to find it.
950 pRegistry->Write(KEY_NAME, plug.GetSymbol().Msgid().MSGID());
951
952 pRegistry->Write(KEY_VERSION, plug.GetUntranslatedVersion());
953 pRegistry->Write(KEY_VENDOR, plug.GetVendor());
954 // Write a blank -- see comments in LoadGroup:
955 pRegistry->Write(KEY_DESCRIPTION, wxString{});
956 pRegistry->Write(KEY_PROVIDERID, plug.GetProviderID());
957 pRegistry->Write(KEY_ENABLED, plug.IsEnabled());
958 pRegistry->Write(KEY_VALID, plug.IsValid());
959
960 switch (type)
961 {
962 case PluginTypeModule:
963 break;
964
965 case PluginTypeEffect:
966 {
967 EffectType etype = plug.GetEffectType();
968 wxString stype;
969 if (etype == EffectTypeNone)
970 stype = KEY_EFFECTTYPE_NONE;
971 else if (etype == EffectTypeAnalyze)
973 else if (etype == EffectTypeGenerate)
975 else if (etype == EffectTypeProcess)
977 else if (etype == EffectTypeTool)
978 stype = KEY_EFFECTTYPE_TOOL;
979 else if (etype == EffectTypeHidden)
980 stype = KEY_EFFECTTYPE_HIDDEN;
981
982 pRegistry->Write(KEY_EFFECTTYPE, stype);
983 pRegistry->Write(KEY_EFFECTFAMILY, plug.GetEffectFamily());
984 pRegistry->Write(KEY_EFFECTDEFAULT, plug.IsEffectDefault());
985 pRegistry->Write(KEY_EFFECTINTERACTIVE, plug.IsEffectInteractive());
986 pRegistry->Write(KEY_EFFECTREALTIME, plug.SerializeRealtimeSupport());
987 pRegistry->Write(KEY_EFFECTAUTOMATABLE, plug.IsEffectAutomatable());
988 }
989 break;
990
992 {
993 pRegistry->Write(KEY_IMPORTERIDENT, plug.GetImporterIdentifier());
994 const auto & extensions = plug.GetImporterExtensions();
995 wxString strExt;
996 for (size_t i = 0, cnt = extensions.size(); i < cnt; i++)
997 {
998 strExt += extensions[i] + wxT(":");
999 }
1000 strExt.RemoveLast(1);
1001 pRegistry->Write(KEY_IMPORTEREXTENSIONS, strExt);
1002 }
1003 break;
1004
1005 default:
1006 break;
1007 }
1008 }
1009
1010 return;
1011}
1012
1013// Here solely for the purpose of Nyquist Workbench until
1014// a better solution is devised.
1016 std::unique_ptr<EffectDefinitionInterface> effect, PluginType type)
1017{
1018 PluginDescriptor & plug =
1019 CreatePlugin(GetID(effect.get()), effect.get(), type);
1020
1021 plug.SetEffectType(effect->GetType());
1022 plug.SetEffectFamily(effect->GetFamily().Internal());
1023 plug.SetEffectInteractive(effect->IsInteractive());
1024 plug.SetEffectDefault(effect->IsDefault());
1025 plug.SetRealtimeSupport(effect->RealtimeSupport());
1026 plug.SetEffectAutomatable(effect->SupportsAutomation());
1027
1028 plug.SetEffectLegacy(true);
1029 plug.SetEnabled(true);
1030 plug.SetValid(true);
1031
1032 mLoadedInterfaces[plug.GetID()] = std::move(effect);
1033
1034 return plug.GetID();
1035}
1036
1038{
1039 mRegisteredPlugins.erase(ID);
1040 mLoadedInterfaces.erase(ID);
1041}
1042
1044{
1045 return count_if(mRegisteredPlugins.begin(), mRegisteredPlugins.end(), [type](auto &pair){
1046 return pair.second.GetPluginType() == type; });
1047}
1048
1050{
1051 if (auto iter = mRegisteredPlugins.find(ID); iter != mRegisteredPlugins.end())
1052 return &iter->second;
1053
1055 .find_if([&ID](const PluginDescriptor& plug) {
1056 return plug.GetID() == ID;
1057 });
1058 if (iter2 != mEffectPluginsCleared.end())
1059 return &(*iter2);
1060
1061 return nullptr;
1062}
1063
1065{
1066 const auto end = mPm.mRegisteredPlugins.end();
1067 if (incrementing && mIterator != end)
1068 ++mIterator;
1070 for (; mIterator != end; ++mIterator) {
1071 auto &plug = mIterator->second;
1072 if (!all && !(plug.IsValid() && plug.IsEnabled()))
1073 continue;
1074 auto plugType = plug.GetPluginType();
1075 if ((mPluginType == PluginTypeNone || (plugType & mPluginType)) &&
1076 (mEffectType == EffectTypeNone || plug.GetEffectType() == mEffectType)) {
1077 if (!all && (plugType & PluginTypeEffect)) {
1078 // This preference may be written by EffectsPrefs
1079 auto setting = mPm.GetPluginEnabledSetting( plug );
1080 if (!(setting.empty() || gPrefs->Read( setting, true )))
1081 continue;
1082 }
1083 // Pause iteration at this match
1084 break;
1085 }
1086 }
1087}
1088
1090: mPm{ manager }
1091, mIterator{ manager.mRegisteredPlugins.begin() }
1092{
1093}
1094
1096: mPm{ manager }
1097, mIterator{ manager.mRegisteredPlugins.begin() }
1098, mPluginType{ type }
1099{
1100 Advance(false);
1101}
1102
1104: mPm{ manager }
1105, mIterator{ manager.mRegisteredPlugins.begin() }
1106, mEffectType{ type }
1107{
1108 Advance(false);
1109}
1110
1112{
1113 Advance(true);
1114 return *this;
1115}
1116
1118{
1119 if (auto iter = mRegisteredPlugins.find(ID); iter == mRegisteredPlugins.end())
1120 return false;
1121 else
1122 return iter->second.IsEnabled();
1123}
1124
1125void PluginManager::EnablePlugin(const PluginID & ID, bool enable)
1126{
1127 if (auto iter = mRegisteredPlugins.find(ID); iter == mRegisteredPlugins.end())
1128 return;
1129 else
1130 iter->second.SetEnabled(enable);
1131}
1132
1134{
1135 if (auto iter = mRegisteredPlugins.find(ID); iter == mRegisteredPlugins.end()) {
1136 static ComponentInterfaceSymbol empty;
1137 return empty;
1138 }
1139 else
1140 return iter->second.GetSymbol();
1141}
1142
1144{
1145 if(auto it = mLoadedInterfaces.find(ID); it != mLoadedInterfaces.end())
1146 return it->second.get();
1147
1148 if(auto it = mRegisteredPlugins.find(ID); it != mRegisteredPlugins.end())
1149 {
1150 auto& desc = it->second;
1151 if(desc.GetPluginType() == PluginTypeModule)
1152 //it's very likely that this code path is not used
1153 return ModuleManager::Get().CreateProviderInstance(desc.GetID(), desc.GetPath());
1154
1155 if(auto provider = ModuleManager::Get().CreateProviderInstance(desc.GetProviderID(), wxEmptyString))
1156 {
1157 auto pluginInterface = provider->LoadPlugin(desc.GetPath());
1158 auto result = pluginInterface.get();
1159 mLoadedInterfaces[desc.GetID()] = std::move(pluginInterface);
1160 return result;
1161 }
1162 }
1163 return nullptr;
1164}
1165
1167{
1168 mEffectPluginsCleared.clear();
1169
1170 for ( auto it = mRegisteredPlugins.cbegin(); it != mRegisteredPlugins.cend(); )
1171 {
1172 const auto& desc = it->second;
1173 const auto type = desc.GetPluginType();
1174
1175 if (type == PluginTypeEffect || type == PluginTypeStub)
1176 {
1177 mEffectPluginsCleared.push_back(desc);
1178 it = mRegisteredPlugins.erase(it);
1179 }
1180 else
1181 {
1182 ++it;
1183 }
1184 }
1185
1186 // Repeat what usually happens at startup
1187 // This prevents built-in plugins to appear in the plugin validation list
1188 for (auto& [_, provider] : ModuleManager::Get().Providers())
1189 provider->AutoRegisterPlugins(*this);
1190
1191 // Remove auto registered plugins from "cleared" list
1192 for ( auto it = mEffectPluginsCleared.begin(); it != mEffectPluginsCleared.end(); )
1193 {
1194 if ( mRegisteredPlugins.find(it->GetID()) != mRegisteredPlugins.end() )
1195 it = mEffectPluginsCleared.erase(it);
1196 else
1197 ++it;
1198 }
1199}
1200
1201std::map<wxString, std::vector<wxString>> PluginManager::CheckPluginUpdates()
1202{
1203 wxArrayString pathIndex;
1204 for (auto &pair : mRegisteredPlugins) {
1205 auto &plug = pair.second;
1206
1207 // Bypass 2.1.0 placeholders...remove this after a few releases past 2.1.0
1208 if (plug.GetPluginType() != PluginTypeNone)
1209 pathIndex.push_back(plug.GetPath().BeforeFirst(wxT(';')));
1210 }
1211
1212 // Scan for NEW ones.
1213 //
1214 // Because we use the plugins "path" as returned by the providers, we can actually
1215 // have multiple providers report the same path since, at this point, they only
1216 // know that the path might possibly be one supported by the provider.
1217 //
1218 // When the user enables the plugin, each provider that reported it will be asked
1219 // to register the plugin.
1220
1221 auto& moduleManager = ModuleManager::Get();
1222 std::map<wxString, std::vector<wxString>> newPaths;
1223 for(auto& [id, provider] : moduleManager.Providers())
1224 {
1225 const auto paths = provider->FindModulePaths(*this);
1226 for(const auto& path : paths)
1227 {
1228 const auto modulePath = path.BeforeFirst(';');
1229 if (!make_iterator_range(pathIndex).contains(modulePath) ||
1230 make_iterator_range(mEffectPluginsCleared).any_of([&modulePath](const PluginDescriptor& plug) {
1231 return plug.GetPath().BeforeFirst(wxT(';')) == modulePath;
1232 })
1233 )
1234 {
1235 newPaths[modulePath].push_back(id);
1236 }
1237 }
1238 }
1239
1240 return newPaths;
1241}
1242
1244{
1245 return ModuleManager::GetID(provider);
1246}
1247
1249{
1250 return wxString::Format(wxT("%s_%s_%s_%s_%s"),
1252 wxEmptyString,
1253 command->GetVendor().Internal(),
1254 command->GetSymbol().Internal(),
1255 command->GetPath());
1256}
1257
1259{
1260 return wxString::Format(wxT("%s_%s_%s_%s_%s"),
1262 effect->GetFamily().Internal(),
1263 effect->GetVendor().Internal(),
1264 effect->GetSymbol().Internal(),
1265 effect->GetPath());
1266}
1267
1269{
1270 return wxJoin(wxArrayStringEx{
1272 effect->GetFamily().Internal(),
1273 effect->GetVendor().Internal(),
1274 effect->GetSymbol().Internal(),
1275 effect->GetPath()
1276 }, '_');
1277}
1278
1280{
1281 auto strings = wxSplit(ID, '_');
1282 if (strings.size() == 5)
1283 return strings[3];
1284 return {};
1285}
1286
1287// This string persists in configuration files
1288// So config compatibility will break if it is changed across Audacity versions
1290{
1291 wxString str;
1292
1293 switch (type)
1294 {
1295 default:
1296 case PluginTypeNone:
1297 str = wxT("Placeholder");
1298 break;
1299 case PluginTypeStub:
1300 str = wxT("Stub");
1301 break;
1302 case PluginTypeEffect:
1303 str = wxT("Effect");
1304 break;
1306 str = wxT("Generic");
1307 break;
1308 case PluginTypeExporter:
1309 str = wxT("Exporter");
1310 break;
1311 case PluginTypeImporter:
1312 str = wxT("Importer");
1313 break;
1314 case PluginTypeModule:
1316 break;
1317 }
1318
1319 return str;
1320}
1321
1323{
1324 const auto& providerID = plug.GetProviderID();
1325 auto provider = ModuleManager::Get().CreateProviderInstance(providerID, wxEmptyString);
1326
1327 if (provider == nullptr)
1328 {
1329 wxLogWarning("Unable to find a provider for '%s'", providerID);
1330 return false;
1331 }
1332
1333 if (provider->CheckPluginExist(plug.GetPath()) == false)
1334 {
1335 wxLogWarning("Plugin '%s' does not exist", plug.GetID());
1336 return false;
1337 }
1338
1339 return true;
1340}
1341
1344 PluginType type)
1345{
1346 // This will either create a NEW entry or replace an existing entry
1348
1349 plug.SetPluginType(type);
1350
1351 plug.SetID(id);
1352 plug.SetPath(ident->GetPath());
1353 plug.SetSymbol(ident->GetSymbol());
1354 plug.SetVendor(ident->GetVendor().Internal());
1355 plug.SetVersion(ident->GetVersion());
1356
1357 return plug;
1358}
1359
1361{
1362 if (!mSettings)
1363 {
1365
1366 // Check for a settings version that we can understand
1367 if (mSettings->HasEntry(SETVERKEY))
1368 {
1369 wxString setver = mSettings->Read(SETVERKEY, SETVERKEY);
1370 if (setver < SETVERCUR )
1371 {
1372 // This is where we'd put in conversion code when the
1373 // settings version changes.
1374 //
1375 // Should also check for a settings file that is newer than
1376 // what we can understand.
1377 }
1378 }
1379 else
1380 {
1381 // Make sure is has a version string
1382 mSettings->Write(SETVERKEY, SETVERCUR);
1383 mSettings->Flush();
1384 }
1385 }
1386
1387 return mSettings.get();
1388}
1389
1391{
1392 auto settings = GetSettings();
1393
1394 if(!settings->HasGroup(groupName))
1395 return false;
1396
1397 auto group = settings->BeginGroup(groupName);
1398 return !settings->GetChildGroups().empty() || !settings->GetChildKeys().empty();
1399}
1400
1401bool PluginManager::GetSubgroups(const RegistryPath & groupName, RegistryPaths & subgroups)
1402{
1403 if (groupName.empty() || !HasGroup(groupName))
1404 {
1405 return false;
1406 }
1407
1408 auto group = GetSettings()->BeginGroup(groupName);
1409 for(const auto& name : GetSettings()->GetChildGroups())
1410 subgroups.push_back(name);
1411
1412 return true;
1413}
1414
1416{
1417 return GetSettings()->Exists(key);
1418}
1419
1420template<typename T> class TD;
1421
1424{
1425 using namespace Variant;
1426 if (key.empty())
1427 return false;
1428 const auto visitor = [&](const auto var){
1429 const auto pVar = &var.get();
1430 // precondition is that defval wraps same type as var
1431 using Type = typename decltype(var)::type;
1432 const auto pDefval =
1433 std::get_if<std::reference_wrapper<const Type>>(&defval);
1434 //TD<decltype(pDefval)> defType;
1435 //return true;
1436 return GetSettings()->Read(key, pVar, pDefval->get());
1437 };
1438 return Visit(visitor, var);
1439}
1440
1442 const RegistryPath & key, ConfigConstReference value)
1443{
1444 using namespace Variant;
1445 if (key.empty())
1446 return false;
1447 const auto visitor = [&](const auto value){
1448 return GetSettings()->Write(key, value.get()) && GetSettings()->Flush();
1449 };
1450 return Visit(visitor, value);
1451}
1452
1453/* Return value is a key for lookup in a config file */
1455 ConfigurationType type, const PluginID & ID)
1456{
1457 bool shared = (type == ConfigurationType::Shared);
1458
1459 // All the strings reported by PluginDescriptor and used in this function
1460 // persist in the plugin settings configuration file, so they should not
1461 // be changed across Audacity versions, or else compatibility of the
1462 // configuration files will break.
1463
1464 if (auto iter = mRegisteredPlugins.find(ID); iter == mRegisteredPlugins.end())
1465 return {};
1466 else {
1467 const PluginDescriptor & plug = iter->second;
1468
1469 wxString id = GetPluginTypeString(plug.GetPluginType()) +
1470 wxT("_") +
1471 plug.GetEffectFamily() + // is empty for non-Effects
1472 wxT("_") +
1473 plug.GetVendor() +
1474 wxT("_") +
1475 (shared ? wxString{} : plug.GetSymbol().Internal());
1476
1477 return SETROOT +
1478 ConvertID(id) +
1479 wxCONFIG_PATH_SEPARATOR +
1480 (shared ? wxT("shared") : wxT("private")) +
1481 wxCONFIG_PATH_SEPARATOR;
1482 }
1483}
1484
1485/* Return value is a key for lookup in a config file */
1487 const PluginID & ID, const RegistryPath & group)
1488{
1489 auto path = SettingsPath(type, ID);
1490
1491 wxFileName ff(group);
1492 if (!ff.GetName().empty())
1493 {
1494 path += ff.GetFullPath(wxPATH_UNIX) + wxCONFIG_PATH_SEPARATOR;
1495 }
1496
1497 return path;
1498}
1499
1500/* Return value is a key for lookup in a config file */
1502 const RegistryPath & group, const RegistryPath & key)
1503{
1504 auto path = Group(type, ID, group);
1505 if (path.empty())
1506 {
1507 return path;
1508 }
1509
1510 return path + key;
1511}
1512
1513// Sanitize the ID...not the best solution, but will suffice until this
1514// is converted to XML. We use base64 encoding to preserve case.
1516{
1517 if (ID.StartsWith(wxT("base64:")))
1518 {
1519 wxString id = ID.Mid(7);
1520 ArrayOf<char> buf{ id.length() / 4 * 3 };
1521 id = wxString::FromUTF8(buf.get(), Base64::Decode(id, buf.get()));
1522 return id;
1523 }
1524
1525 const wxCharBuffer & buf = ID.ToUTF8();
1526 return wxT("base64:") + Base64::Encode(buf, strlen(buf));
1527}
1528
1529// This is defined out-of-line here, to keep ComponentInterface free of other
1530// #include directives.
1532{
1533 return GetSymbol().Msgid();
1534}
wxT("CloseDown"))
@ Internal
Indicates internal failure from Audacity.
static RegisteredToolbarFactory factory
Toolkit-neutral facade for basic user interface services.
wxString PluginID
#define str(a)
const TranslatableString name
Definition: Distortion.cpp:76
EffectType
@ EffectTypeHidden
@ EffectTypeAnalyze
@ EffectTypeGenerate
@ EffectTypeNone
@ EffectTypeTool
@ EffectTypeProcess
XO("Cut/Copy/Paste")
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 XPC(sing, plur, n, c)
Definition: Internat.h:98
#define _(s)
Definition: Internat.h:73
IteratorRange< Iterator > make_iterator_range(const Iterator &i1, const Iterator &i2)
Definition: IteratorX.h:210
#define safenew
Definition: MemoryX.h:10
static CommandHandlerObject & ident(AudacityProject &project)
static const AudacityProject::AttachedObjects::RegisteredFactory key
PluginType
@ PluginTypeStub
@ PluginTypeModule
@ PluginTypeAudacityCommand
@ PluginTypeExporter
@ PluginTypeNone
@ PluginTypeEffect
@ PluginTypeImporter
bool Regver_lt(const PluginRegistryVersion &regver1, const PluginRegistryVersion &regver2)
bool Regver_le(const PluginRegistryVersion &regver1, const PluginRegistryVersion &regver2)
wxString PluginRegistryVersion
Type of plugin registry version information.
#define KEY_EFFECTAUTOMATABLE
#define REGVERKEY
#define KEY_PROVIDERID
#define KEY_SYMBOL
#define KEY_IMPORTEREXTENSIONS
#define KEY_EFFECTTYPE_TOOL
#define KEY_VENDOR
#define KEY_PATH
#define REGCUSTOMPATHS
#define KEY_IMPORTERIDENT
static PluginManager::ConfigFactory sFactory
#define KEY_EFFECTTYPE_GENERATE
#define SETROOT
#define KEY_EFFECTTYPE_PROCESS
#define KEY_EFFECTINTERACTIVE
#define KEY_EFFECTTYPE_HIDDEN
#define KEY_ENABLED
#define SETVERKEY
#define SETVERCUR
#define KEY_EFFECTREALTIME
#define REGROOT
#define KEY_VERSION
#define KEY_EFFECTTYPE_NONE
#define KEY_EFFECTTYPE
#define KEY_EFFECTFAMILY
#define KEY_NAME
#define KEY_VALID
#define KEY_EFFECTTYPE_ANALYZE
#define KEY_DESCRIPTION
#define KEY_EFFECTDEFAULT
constexpr auto REGVERCUR
#define NYQUIST_PROMPT_ID
Generalized interface for discovery of plug-ins for one protocol.
audacity::BasicSettings * gPrefs
Definition: Prefs.cpp:68
static const AttachedProjectObjects::RegisteredFactory manager
static TranslatableStrings names
Definition: TagsEditor.cpp:153
static Settings & settings()
Definition: TrackInfo.cpp:51
TranslatableString Verbatim(wxString str)
Require calls to the one-argument constructor to go through this distinct global function name.
emulates std::visit for one visitor
int id
ComponentInterface provides name / vendor / version functions to identify plugins....
virtual PluginPath GetPath() const =0
virtual VendorSymbol GetVendor() const =0
TranslatableString GetName() const
virtual ComponentInterfaceSymbol GetSymbol() const =0
ComponentInterfaceSymbol pairs a persistent string identifier used internally with an optional,...
const wxString & Internal() const
const TranslatableString & Msgid() const
EffectDefinitionInterface is a ComponentInterface that adds some basic read-only information about ef...
virtual EffectType GetClassification() const
Determines which menu it appears in; default same as GetType().
virtual bool IsDefault() const =0
Whether the effect sorts "above the line" in the menus.
virtual bool IsInteractive() const =0
Whether the effect needs a dialog for entry of settings.
virtual bool SupportsAutomation() const =0
Whether the effect has any automatable controls.
virtual RealtimeSince RealtimeSupport() const =0
Since which version of Audacity has the effect supported realtime?
virtual EffectFamilySymbol GetFamily() const =0
Report identifier and user-visible name of the effect protocol.
Abstract base class used in importing a file.
An explicitly nonlocalized string, not meant for the user to see.
Definition: Identifier.h:22
static ModuleManager & Get()
static wxString GetPluginTypeString()
static PluginID GetID(const PluginProvider *provider)
PluginProvider * CreateProviderInstance(const PluginID &provider, const PluginPath &path)
bool CheckPluginExist(const PluginID &providerId, const PluginPath &path)
CallbackReturn Publish(const PluginsChangedMessage &message)
Send a message to connected callbacks.
Definition: Observer.h:207
static const FilePath & GetExecutablePath()
void SetEnabled(bool enable)
void SetEffectLegacy(bool legacy)
void SetImporterExtensions(FileExtensions extensions)
const ComponentInterfaceSymbol & GetSymbol() const
PluginType GetPluginType() const
void SetVendor(const wxString &vendor)
void SetImporterIdentifier(const wxString &identifier)
wxString GetEffectFamily() const
void SetPath(const PluginPath &path)
void SetValid(bool valid)
const wxString & GetID() const
const wxString & GetVendor() const
void SetSymbol(const ComponentInterfaceSymbol &symbol)
void SetProviderID(const PluginID &providerID)
const PluginPath & GetPath() const
void SetID(const PluginID &ID)
void SetEffectType(EffectType type)
void SetEffectFamily(const wxString &family)
void SetPluginType(PluginType type)
const wxString & GetProviderID() const
void SetEffectAutomatable(bool automatable)
void SetEffectDefault(bool dflt)
void SetEffectInteractive(bool interactive)
void SetRealtimeSupport(EffectDefinitionInterface::RealtimeSince realtime)
void SetVersion(const wxString &version)
void DeserializeRealtimeSupport(const wxString &value)
for deserialization
void Advance(bool incrementing)
Iterator(PluginManager &manager)
Iterates all, even disabled.
const PluginManager & mPm
PluginMap::iterator mIterator
PluginManager maintains a list of all plug ins. That covers modules, effects, generators,...
Definition: PluginManager.h:51
static PluginID OldGetID(const EffectDefinitionInterface *effect)
bool GetConfigSubgroups(ConfigurationType type, const PluginID &ID, const RegistryPath &group, RegistryPaths &subgroups) override
bool HasConfigGroup(ConfigurationType type, const PluginID &ID, const RegistryPath &group)
bool IsPluginEnabled(const PluginID &ID)
bool GetConfigValue(ConfigurationType type, const PluginID &ID, const RegistryPath &group, const RegistryPath &key, ConfigReference var, ConfigConstReference defval) override
std::vector< PluginDescriptor > mEffectPluginsCleared
void ClearEffectPlugins()
PluginRegistryVersion mRegver
static PluginID GetID(const PluginProvider *provider)
std::map< wxString, std::vector< wxString > > CheckPluginUpdates()
Ensures that all currently registered plugins still exist and scans for new ones.
bool IsPluginRegistered(const PluginPath &path, const TranslatableString *pSymbol) override
Was the plugin registry already populated for a path (maybe from loading the config file)?
bool IsPluginLoaded(const wxString &ID) const
void StoreCustomPaths(const PluginProvider &provider, const PluginPaths &paths) override
RegistryPath Group(ConfigurationType type, const PluginID &ID, const RegistryPath &group)
bool HasConfigValue(ConfigurationType type, const PluginID &ID, const RegistryPath &group, const RegistryPath &key) override
std::function< std::unique_ptr< audacity::BasicSettings >(const FilePath &localFilename) > ConfigFactory
void Save()
Save to preferences.
void UnregisterPlugin(const PluginID &ID)
void FindFilesInPathList(const wxString &pattern, const FilePaths &pathList, FilePaths &files, bool directories=false) override
bool GetSubgroups(const RegistryPath &group, RegistryPaths &subgroups)
static Identifier GetEffectNameFromID(const PluginID &ID)
bool HasGroup(const RegistryPath &group)
void LoadGroup(audacity::BasicSettings *pRegistry, PluginType type)
void Load()
Load from preferences.
Range PluginsOfType(int type)
PluginDescriptor & CreatePlugin(const PluginID &id, ComponentInterface *ident, PluginType type)
static bool IsPluginAvailable(const PluginDescriptor &plug)
const PluginRegistryVersion & GetRegistryVersion() const override
RegistryPath Key(ConfigurationType type, const PluginID &ID, const RegistryPath &group, const RegistryPath &key)
static std::unique_ptr< PluginManager > mInstance
void Initialize(ConfigFactory factory)
RegistryPath GetPluginEnabledSetting(const PluginID &ID) const
PluginPaths ReadCustomPaths(const PluginProvider &provider) override
bool DropFile(const wxString &fileName)
const PluginDescriptor * GetPlugin(const PluginID &ID) const
void EnablePlugin(const PluginID &ID, bool enable)
void RegisterPlugin(PluginDescriptor &&desc)
bool RemoveConfig(ConfigurationType type, const PluginID &ID, const RegistryPath &group, const RegistryPath &key) override
audacity::BasicSettings * GetSettings()
void SaveGroup(audacity::BasicSettings *pRegistry, PluginType type)
PluginMap mRegisteredPlugins
void InitializePlugins()
wxString ConvertID(const PluginID &ID)
std::unique_ptr< audacity::BasicSettings > mSettings
RegistryPath SettingsPath(ConfigurationType type, const PluginID &ID)
bool SetConfigValue(ConfigurationType type, const PluginID &ID, const RegistryPath &group, const RegistryPath &key, ConfigConstReference value) override
int GetPluginCount(PluginType type)
static wxString GetPluginTypeString(PluginType type)
void NotifyPluginsChanged()
bool RemoveConfigSubgroup(ConfigurationType type, const PluginID &ID, const RegistryPath &group) override
std::map< PluginID, std::unique_ptr< ComponentInterface > > mLoadedInterfaces
const ComponentInterfaceSymbol & GetSymbol(const PluginID &ID)
static PluginManager & Get()
static const PluginID & AudacityCommandRegistrationCallback(PluginProvider *provider, ComponentInterface *ident)
PluginSettings::ConfigReference ConfigReference
static const PluginID & DefaultRegistrationCallback(PluginProvider *provider, ComponentInterface *ident)
PluginSettings::ConfigConstReference ConfigConstReference
virtual EffectFamilySymbol GetOptionalFamilySymbol()=0
A symbol identifying the family of plug-ins provided by this.
virtual std::unique_ptr< ComponentInterface > LoadPlugin(const PluginPath &path)=0
Load the plug-in at a path reported by DiscoverPluginsAtPath.
virtual FilePath InstallPath()=0
Where plug-in files should be copied to install them.
Holds a msgid for the translation catalog; may also bind format arguments.
Base class for objects that provide facility to store data persistently, and access it with string ke...
Definition: BasicSettings.h:31
virtual bool Flush() noexcept=0
virtual bool Exists(const wxString &key) const
Returns true if group or entry exists.
GroupScope BeginGroup(const wxString &prefix)
Appends a prefix to the current group or sets a new absolute path. Group that was set as current befo...
bool DeleteGroup(const wxString &key)
Deletes specified group if exists.
virtual bool Write(const wxString &key, bool value)=0
bool DeleteEntry(const wxString &key)
Deletes specified entry if exists.
virtual bool Read(const wxString &key, bool *value) const =0
virtual wxArrayString GetChildGroups() const =0
Returns all child groups within the current group.
Extend wxArrayString with move operations and construction and insertion fromstd::initializer_list.
STRINGS_API wxString Encode(const void *in, int len)
Definition: Base64.cpp:27
STRINGS_API int Decode(const wxString &in, void *out)
Definition: Base64.cpp:67
MessageBoxResult ShowMessageBox(const TranslatableString &message, MessageBoxOptions options={})
Show a modal message box with either Ok or Yes and No, and optionally Cancel.
Definition: BasicUI.h:287
FILES_API FilePath PlugInDir()
The user plug-in directory (not a system one)
FILES_API bool DoCopyFile(const FilePath &file1, const FilePath &file2, bool overwrite=true)
FILES_API FilePath PluginRegistry()
FILES_API FilePath PluginSettings()
MENUS_API void Visit(Visitor< Traits > &visitor, AudacityProject &project)
const TranslatableString desc
Definition: ExportPCM.cpp:51
const char * end(const char *str) noexcept
Definition: StringUtils.h:106
const char * begin(const char *str) noexcept
Definition: StringUtils.h:101
void copy(const T *src, T *dst, int32_t n)
Definition: VectorOps.h:40
MessageBoxOptions && Caption(TranslatableString caption_) &&
Definition: BasicUI.h:101