From 6463e12576116d775c210b9f050364d55d121146 Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Thu, 28 Dec 2017 16:58:42 -0500 Subject: [PATCH 1/3] Move MyCopyFile into class FileNames --- src/DirManager.cpp | 35 ++++------------------------------- src/FileNames.cpp | 26 ++++++++++++++++++++++++++ src/FileNames.h | 4 ++++ 3 files changed, 34 insertions(+), 31 deletions(-) diff --git a/src/DirManager.cpp b/src/DirManager.cpp index e08064940..7efff54b6 100644 --- a/src/DirManager.cpp +++ b/src/DirManager.cpp @@ -91,6 +91,7 @@ #include "AudacityException.h" #include "BlockFile.h" #include "FileException.h" +#include "FileNames.h" #include "blockfile/LegacyBlockFile.h" #include "blockfile/LegacyAliasBlockFile.h" #include "blockfile/SimpleBlockFile.h" @@ -1161,34 +1162,6 @@ bool DirManager::ContainsBlockFile(const wxString &filepath) const BlockFilePtr{ it->second.lock() }; } -namespace { - bool MyCopyFile(const wxString& file1, const wxString& file2, - bool overwrite = true) - { -#ifdef __WXMSW__ - - // workaround not needed - return wxCopyFile(file1, file2, overwrite); - -#else - // PRL: Compensate for buggy wxCopyFile that returns false success, - // which was a cause of case 4 in comment 10 of - // http://bugzilla.audacityteam.org/show_bug.cgi?id=1759 - // Destination file was created, but was empty - // Bug was introduced after wxWidgets 2.8.12 at commit - // 0597e7f977c87d107e24bf3e95ebfa3d60efc249 of wxWidgets repo - - bool existed = wxFileExists(file2); - bool result = wxCopyFile(file1, file2, overwrite) && - wxFile{ file1 }.Length() == wxFile{ file2 }.Length(); - if (!result && !existed) - wxRemoveFile(file2); - return result; - -#endif - } -} - // Adds one to the reference count of the block file, // UNLESS it is "locked", then it makes a NEW copy of // the BlockFile. @@ -1232,7 +1205,7 @@ BlockFilePtr DirManager::CopyBlockFile(const BlockFilePtr &b) //a summary file, so we should check before we copy. if(b->IsSummaryAvailable()) { - if( !MyCopyFile(fn.GetFullPath(), + if( !FileNames::CopyFile(fn.GetFullPath(), newFile.GetFullPath()) ) // Disk space exhaustion, maybe throw FileException{ @@ -1384,7 +1357,7 @@ std::pair DirManager::CopyToNewProjectDirectory(BlockFile *f) bool summaryExisted = f->IsSummaryAvailable(); auto oldPath = oldFileNameRef.GetFullPath(); if (summaryExisted) { - auto success = MyCopyFile(oldPath, newPath); + auto success = FileNames::CopyFile(oldPath, newPath); if (!success) return { false, {} }; } @@ -1415,7 +1388,7 @@ std::pair DirManager::CopyToNewProjectDirectory(BlockFile *f) //if it doesn't, we can assume it was written to the NEW name, which is fine. if (oldFileName.FileExists()) { - bool ok = MyCopyFile(oldPath, newPath); + bool ok = FileNames::CopyFile(oldPath, newPath); if (!ok) return { false, {} }; } diff --git a/src/FileNames.cpp b/src/FileNames.cpp index 6743b3739..041ba11d4 100644 --- a/src/FileNames.cpp +++ b/src/FileNames.cpp @@ -43,6 +43,32 @@ used throughout Audacity into this one place. static wxString gDataDir; +bool FileNames::CopyFile( + const wxString& file1, const wxString& file2, bool overwrite) +{ +#ifdef __WXMSW__ + + // workaround not needed + return wxCopyFile(file1, file2, overwrite); + +#else + // PRL: Compensate for buggy wxCopyFile that returns false success, + // which was a cause of case 4 in comment 10 of + // http://bugzilla.audacityteam.org/show_bug.cgi?id=1759 + // Destination file was created, but was empty + // Bug was introduced after wxWidgets 2.8.12 at commit + // 0597e7f977c87d107e24bf3e95ebfa3d60efc249 of wxWidgets repo + + bool existed = wxFileExists(file2); + bool result = wxCopyFile(file1, file2, overwrite) && + wxFile{ file1 }.Length() == wxFile{ file2 }.Length(); + if (!result && !existed) + wxRemoveFile(file2); + return result; + +#endif +} + wxString FileNames::MkDir(const wxString &Str) { // Behaviour of wxFileName::DirExists() and wxFileName::MkDir() has diff --git a/src/FileNames.h b/src/FileNames.h index 4d290b6ef..47989606c 100644 --- a/src/FileNames.h +++ b/src/FileNames.h @@ -23,6 +23,10 @@ class wxArrayString; class AUDACITY_DLL_API FileNames { public: + // This exists to compensate for bugs in wxCopyFile: + static bool CopyFile( + const wxString& file1, const wxString& file2, bool overwrite = true); + static wxString MkDir(const wxString &Str); static wxString TempDir(); From 8cfb8d2400d993d9dd4258e49c367f15110dfe7c Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Thu, 28 Dec 2017 18:29:03 -0500 Subject: [PATCH 2/3] Change ModuleInterface for support of drag-and-drop of plug-in files --- include/audacity/ModuleInterface.h | 46 ++++++++++++++++++---- include/audacity/PluginInterface.h | 3 ++ src/ModuleManager.cpp | 9 +++-- src/PluginManager.cpp | 6 +++ src/effects/LoadEffects.cpp | 17 ++++---- src/effects/LoadEffects.h | 11 ++++-- src/effects/VST/VSTEffect.cpp | 21 +++++++--- src/effects/VST/VSTEffect.h | 11 ++++-- src/effects/audiounits/AudioUnitEffect.cpp | 17 ++++---- src/effects/audiounits/AudioUnitEffect.h | 11 ++++-- src/effects/ladspa/LadspaEffect.cpp | 38 ++++++++++-------- src/effects/ladspa/LadspaEffect.h | 11 ++++-- src/effects/lv2/LoadLV2.cpp | 13 +++--- src/effects/lv2/LoadLV2.h | 11 ++++-- src/effects/nyquist/LoadNyquist.cpp | 26 +++++++----- src/effects/nyquist/LoadNyquist.h | 11 ++++-- src/effects/vamp/LoadVamp.cpp | 13 +++--- src/effects/vamp/LoadVamp.h | 11 ++++-- 18 files changed, 194 insertions(+), 92 deletions(-) diff --git a/include/audacity/ModuleInterface.h b/include/audacity/ModuleInterface.h index 7f9f62f4b..68f624f59 100644 --- a/include/audacity/ModuleInterface.h +++ b/include/audacity/ModuleInterface.h @@ -42,6 +42,14 @@ #ifndef __AUDACITY_MODULEINTERFACE_H__ #define __AUDACITY_MODULEINTERFACE_H__ +#include + +#ifdef __WXMAC__ +// Needs this for std::function +// Make this go away when Mac moves to a proper C++11 library +#include "../../src/MemoryX.h" +#endif + #include "audacity/Types.h" #include "audacity/IdentInterface.h" #include "audacity/PluginInterface.h" @@ -75,23 +83,45 @@ public: // Called just prior to deletion to allow releasing any resources. virtual void Terminate() = 0; + // "Paths" returned by FindPluginPaths() and passed back to + // DiscoverPluginsAtPath() have module-specific meaning. + // They are not necessarily file system paths to existent files that + // could be placed in any folder and queried for + // plugin information. This function returns true when that is the case. + virtual bool PathsAreFiles() = 0; + + // Returns empty, or else, where to copy a plug-in file or bundle. + // Drag-and-drop is supported only if PathsAreFiles() is true and this + // function returns nonempty. + virtual wxString InstallPath() = 0; + // Modules providing a single or static set of plugins may use // AutoRegisterPlugins() to register those plugins. virtual bool AutoRegisterPlugins(PluginManagerInterface & pluginManager) = 0; // For modules providing an interface to other dynamically loaded plugins, // the module returns a list of path names that will be presented to the - // user for enablement. - virtual wxArrayString FindPlugins(PluginManagerInterface & pluginManager) = 0; + // user as "New" for enablement. + virtual wxArrayString FindPluginPaths(PluginManagerInterface & pluginManager) = 0; - // Once the user selects desired paths from FindPlugins(), a call to RegisterPlugin() - // will be made to request registration of that plugin. If the module must create + // Once the user selects desired paths from FindPluginPaths(), + // a call to DiscoverPluginsAtPath() + // will be made to request registration of one or more plugins. If the module must create // an instance of the plugin to register it, then then instance should be deleted // after registration. - // Error message does not need to mention the path. - virtual bool RegisterPlugin(PluginManagerInterface & pluginManager, - const wxString & path, - wxString &errMsg) = 0; + // May discover more than one plug-in at the path, and + // may call-back with paths not equal to path (perhaps appending + // other information to it). + // Error message does not need to mention the path and may be nonempty + // even if some plugins are also discovered successfully. + // Return value is the number of plugins found. + using RegistrationCallback = + std::function< void(ModuleInterface *, EffectIdentInterface *) >; + virtual unsigned DiscoverPluginsAtPath( + const wxString & path, wxString &errMsg, + const RegistrationCallback &callback = + PluginManagerInterface::DefaultRegistrationCallback) + = 0; // For modules providing an interface to other dynamically loaded plugins, // the module returns true if the plugin is still valid, otherwise false. diff --git a/include/audacity/PluginInterface.h b/include/audacity/PluginInterface.h index 489e9959f..9dbd89f38 100644 --- a/include/audacity/PluginInterface.h +++ b/include/audacity/PluginInterface.h @@ -54,6 +54,9 @@ class PluginManagerInterface /* not final */ { public: + static const PluginID &DefaultRegistrationCallback( + ModuleInterface *provider, EffectIdentInterface *ident ); + virtual bool IsPluginRegistered(const wxString & path) = 0; virtual const PluginID & RegisterPlugin(ModuleInterface *module) = 0; diff --git a/src/ModuleManager.cpp b/src/ModuleManager.cpp index 52e637cca..1db4878e5 100755 --- a/src/ModuleManager.cpp +++ b/src/ModuleManager.cpp @@ -514,7 +514,7 @@ void ModuleManager::FindAllPlugins(PluginIDList & providers, wxArrayString & pat ModuleInterface *module = static_cast(CreateProviderInstance(providerID, modPaths[i])); - wxArrayString newpaths = module->FindPlugins(pm); + wxArrayString newpaths = module->FindPluginPaths(pm); for (size_t i = 0, cnt = newpaths.size(); i < cnt; i++) { providers.push_back(providerID); @@ -536,7 +536,7 @@ wxArrayString ModuleManager::FindPluginsForProvider(const PluginID & providerID, } } - return mDynModules[providerID]->FindPlugins(PluginManager::Get()); + return mDynModules[providerID]->FindPluginPaths(PluginManager::Get()); } bool ModuleManager::RegisterPlugin(const PluginID & providerID, const wxString & path, wxString &errMsg) @@ -547,8 +547,9 @@ bool ModuleManager::RegisterPlugin(const PluginID & providerID, const wxString & return false; } - return mDynModules[providerID]->RegisterPlugin(PluginManager::Get(), path, - errMsg); + auto nFound = mDynModules[providerID]->DiscoverPluginsAtPath(path, errMsg); + + return nFound > 0; } IdentInterface *ModuleManager::CreateProviderInstance(const PluginID & providerID, diff --git a/src/PluginManager.cpp b/src/PluginManager.cpp index f16dcf9bc..930412531 100644 --- a/src/PluginManager.cpp +++ b/src/PluginManager.cpp @@ -1379,6 +1379,12 @@ void PluginDescriptor::SetImporterExtensions(const wxArrayString & extensions) // // ============================================================================ +const PluginID &PluginManagerInterface::DefaultRegistrationCallback( + ModuleInterface *provider, EffectIdentInterface *pInterface ) +{ + return PluginManager::Get().RegisterPlugin(provider, pInterface); +} + bool PluginManager::IsPluginRegistered(const wxString & path) { for (PluginMap::iterator iter = mPlugins.begin(); iter != mPlugins.end(); ++iter) diff --git a/src/effects/LoadEffects.cpp b/src/effects/LoadEffects.cpp index 41310c5c0..3eae7039e 100644 --- a/src/effects/LoadEffects.cpp +++ b/src/effects/LoadEffects.cpp @@ -297,7 +297,8 @@ bool BuiltinEffectsModule::AutoRegisterPlugins(PluginManagerInterface & pm) if (!pm.IsPluginRegistered(path)) { // No checking of error ? - RegisterPlugin(pm, path, ignoredErrMsg); + DiscoverPluginsAtPath(path, ignoredErrMsg, + PluginManagerInterface::DefaultRegistrationCallback); } } @@ -305,25 +306,25 @@ bool BuiltinEffectsModule::AutoRegisterPlugins(PluginManagerInterface & pm) return false; } -wxArrayString BuiltinEffectsModule::FindPlugins(PluginManagerInterface & WXUNUSED(pm)) +wxArrayString BuiltinEffectsModule::FindPluginPaths(PluginManagerInterface & WXUNUSED(pm)) { return mNames; } -bool BuiltinEffectsModule::RegisterPlugin(PluginManagerInterface & pm, - const wxString & path, - wxString &errMsg) +unsigned BuiltinEffectsModule::DiscoverPluginsAtPath( + const wxString & path, wxString &errMsg, + const RegistrationCallback &callback) { errMsg.clear(); auto effect = Instantiate(path); if (effect) { - pm.RegisterPlugin(this, effect.get()); - return true; + callback(this, effect.get()); + return 1; } errMsg = _("Unknown built-in effect name"); - return false; + return 0; } bool BuiltinEffectsModule::IsPluginValid(const wxString & path, bool bFast) diff --git a/src/effects/LoadEffects.h b/src/effects/LoadEffects.h index 9249f51a6..fbf15c4e4 100644 --- a/src/effects/LoadEffects.h +++ b/src/effects/LoadEffects.h @@ -41,10 +41,15 @@ public: bool Initialize() override; void Terminate() override; + bool PathsAreFiles() override { return false; } + wxString InstallPath() override { return {}; } + bool AutoRegisterPlugins(PluginManagerInterface & pm) override; - wxArrayString FindPlugins(PluginManagerInterface & pm) override; - bool RegisterPlugin(PluginManagerInterface & pm, - const wxString & path, wxString &errMsg) override; + wxArrayString FindPluginPaths(PluginManagerInterface & pm) override; + unsigned DiscoverPluginsAtPath( + const wxString & path, wxString &errMsg, + const RegistrationCallback &callback) + override; bool IsPluginValid(const wxString & path, bool bFast) override; diff --git a/src/effects/VST/VSTEffect.cpp b/src/effects/VST/VSTEffect.cpp index 60330a359..21b8c44ca 100644 --- a/src/effects/VST/VSTEffect.cpp +++ b/src/effects/VST/VSTEffect.cpp @@ -351,13 +351,21 @@ void VSTEffectsModule::Terminate() return; } +wxString VSTEffectsModule::InstallPath() +{ + // Not yet ready for VST drag-and-drop... + // return FileNames::PlugInDir(); + + return {}; +} + bool VSTEffectsModule::AutoRegisterPlugins(PluginManagerInterface & WXUNUSED(pm)) { // We don't auto-register return true; } -wxArrayString VSTEffectsModule::FindPlugins(PluginManagerInterface & pm) +wxArrayString VSTEffectsModule::FindPluginPaths(PluginManagerInterface & pm) { wxArrayString pathList; wxArrayString files; @@ -466,10 +474,12 @@ wxArrayString VSTEffectsModule::FindPlugins(PluginManagerInterface & pm) return files; } -bool VSTEffectsModule::RegisterPlugin(PluginManagerInterface & pm, - const wxString & path, wxString &errMsg) +unsigned VSTEffectsModule::DiscoverPluginsAtPath( + const wxString & path, wxString &errMsg, + const RegistrationCallback &callback) { bool error = false; + unsigned nFound = 0; errMsg.clear(); // TODO: Fix this for external usage const wxString &cmdpath = PlatformCompatibility::GetExecutablePath(); @@ -620,7 +630,8 @@ bool VSTEffectsModule::RegisterPlugin(PluginManagerInterface & pm, if (!skip && cont) { valid = true; - pm.RegisterPlugin(this, &proc); + callback( this, &proc ); + ++nFound; } } break; @@ -636,7 +647,7 @@ bool VSTEffectsModule::RegisterPlugin(PluginManagerInterface & pm, if (error) errMsg = _("Could not load the library"); - return valid; + return nFound; } bool VSTEffectsModule::IsPluginValid(const wxString & path, bool bFast) diff --git a/src/effects/VST/VSTEffect.h b/src/effects/VST/VSTEffect.h index 4ecaee64d..9de346fbf 100644 --- a/src/effects/VST/VSTEffect.h +++ b/src/effects/VST/VSTEffect.h @@ -391,10 +391,15 @@ public: bool Initialize() override; void Terminate() override; + bool PathsAreFiles() override { return true; } + wxString InstallPath() override; + bool AutoRegisterPlugins(PluginManagerInterface & pm) override; - wxArrayString FindPlugins(PluginManagerInterface & pm) override; - bool RegisterPlugin(PluginManagerInterface & pm, - const wxString & path, wxString &errMsg) override; + wxArrayString FindPluginPaths(PluginManagerInterface & pm) override; + unsigned DiscoverPluginsAtPath( + const wxString & path, wxString &errMsg, + const RegistrationCallback &callback) + override; bool IsPluginValid(const wxString & path, bool bFast) override; diff --git a/src/effects/audiounits/AudioUnitEffect.cpp b/src/effects/audiounits/AudioUnitEffect.cpp index e7424f04f..00c499067 100644 --- a/src/effects/audiounits/AudioUnitEffect.cpp +++ b/src/effects/audiounits/AudioUnitEffect.cpp @@ -158,7 +158,7 @@ bool AudioUnitEffectsModule::AutoRegisterPlugins(PluginManagerInterface & pm) return true; } -wxArrayString AudioUnitEffectsModule::FindPlugins(PluginManagerInterface & pm) +wxArrayString AudioUnitEffectsModule::FindPluginPaths(PluginManagerInterface & pm) { wxArrayString effects; @@ -171,9 +171,9 @@ wxArrayString AudioUnitEffectsModule::FindPlugins(PluginManagerInterface & pm) return effects; } -bool AudioUnitEffectsModule::RegisterPlugin(PluginManagerInterface & pm, - const wxString & path, - wxString &errMsg) +unsigned AudioUnitEffectsModule::DiscoverPluginsAtPath( + const wxString & path, wxString &errMsg, + const RegistrationCallback &callback) { errMsg.clear(); wxString name; @@ -181,7 +181,7 @@ bool AudioUnitEffectsModule::RegisterPlugin(PluginManagerInterface & pm, if (component == NULL) { errMsg = _("Could not find component"); - return false; + return 0; } AudioUnitEffect effect(path, name, component); @@ -190,12 +190,11 @@ bool AudioUnitEffectsModule::RegisterPlugin(PluginManagerInterface & pm, // TODO: Is it worth it to discriminate all the ways SetHost might // return false? errMsg = _("Could not initialize component"); - return false; + return 0; } - pm.RegisterPlugin(this, &effect); - - return true; + callback(this, &effect); + return 1; } bool AudioUnitEffectsModule::IsPluginValid( diff --git a/src/effects/audiounits/AudioUnitEffect.h b/src/effects/audiounits/AudioUnitEffect.h index b49a348d9..3b5c001b1 100644 --- a/src/effects/audiounits/AudioUnitEffect.h +++ b/src/effects/audiounits/AudioUnitEffect.h @@ -243,10 +243,15 @@ public: bool Initialize() override; void Terminate() override; + bool PathsAreFiles() override { return false; } + wxString InstallPath() override { return {}; } + bool AutoRegisterPlugins(PluginManagerInterface & pm) override; - wxArrayString FindPlugins(PluginManagerInterface & pm) override; - bool RegisterPlugin(PluginManagerInterface & pm, - const wxString & path, wxString &errMsg) override; + wxArrayString FindPluginPaths(PluginManagerInterface & pm) override; + unsigned DiscoverPluginsAtPath( + const wxString & path, wxString &errMsg, + const RegistrationCallback &callback) + override; bool IsPluginValid(const wxString & path, bool bFast) override; diff --git a/src/effects/ladspa/LadspaEffect.cpp b/src/effects/ladspa/LadspaEffect.cpp index 2852c7bee..5aed54616 100644 --- a/src/effects/ladspa/LadspaEffect.cpp +++ b/src/effects/ladspa/LadspaEffect.cpp @@ -49,6 +49,7 @@ effects from this one class. #include #include "LadspaEffect.h" // This class's header file +#include "../../FileNames.h" #include "../../Internat.h" #include "../../ShuttleGui.h" #include "../../widgets/valnum.h" @@ -154,6 +155,12 @@ void LadspaEffectsModule::Terminate() return; } +wxString LadspaEffectsModule::InstallPath() +{ + // To do: better choice + return FileNames::PlugInDir(); +} + bool LadspaEffectsModule::AutoRegisterPlugins(PluginManagerInterface & pm) { // Autoregister effects that we "think" are ones that have been shipped with @@ -171,7 +178,8 @@ bool LadspaEffectsModule::AutoRegisterPlugins(PluginManagerInterface & pm) if (!pm.IsPluginRegistered(files[j])) { // No checking for error ? - RegisterPlugin(pm, files[j], ignoredErrMsg); + DiscoverPluginsAtPath(files[j], ignoredErrMsg, + PluginManagerInterface::DefaultRegistrationCallback); } } } @@ -180,7 +188,7 @@ bool LadspaEffectsModule::AutoRegisterPlugins(PluginManagerInterface & pm) return false; } -wxArrayString LadspaEffectsModule::FindPlugins(PluginManagerInterface & pm) +wxArrayString LadspaEffectsModule::FindPluginPaths(PluginManagerInterface & pm) { wxArrayString pathList = GetSearchPaths(); wxArrayString files; @@ -205,9 +213,9 @@ wxArrayString LadspaEffectsModule::FindPlugins(PluginManagerInterface & pm) return files; } -bool LadspaEffectsModule::RegisterPlugin(PluginManagerInterface & pm, - const wxString & path, - wxString &errMsg) +unsigned LadspaEffectsModule::DiscoverPluginsAtPath( + const wxString & path, wxString &errMsg, + const RegistrationCallback &callback) { errMsg.clear(); // Since we now have builtin VST support, ignore the VST bridge as it @@ -215,7 +223,7 @@ bool LadspaEffectsModule::RegisterPlugin(PluginManagerInterface & pm, wxFileName ff(path); if (ff.GetName().CmpNoCase(wxT("vst-bridge")) == 0) { errMsg = _("Audacity no longer uses vst-bridge"); - return false; + return 0; } // As a courtesy to some plug-ins that might be bridges to @@ -227,8 +235,8 @@ bool LadspaEffectsModule::RegisterPlugin(PluginManagerInterface & pm, wxString saveOldCWD = ff.GetCwd(); ff.SetCwd(); - bool error = false; int index = 0; + int nLoaded = 0; LADSPA_Descriptor_Function mainFn = NULL; wxDynamicLibrary lib; if (lib.Load(path, wxDL_NOW)) { @@ -241,15 +249,16 @@ bool LadspaEffectsModule::RegisterPlugin(PluginManagerInterface & pm, for (data = mainFn(index); data; data = mainFn(++index)) { LadspaEffect effect(path, index); if (effect.SetHost(NULL)) { - pm.RegisterPlugin(this, &effect); - } - else { - // If pm.RegisterPlugin is skipped, be sure to report error - error = true; + ++nLoaded; + callback( this, &effect ); } + else + errMsg = _("Could not load the library"); } } } + else + errMsg = _("Could not load the library"); if (lib.IsLoaded()) { // PRL: I suspect Bug1257 -- Crash when enabling Amplio2 -- is the fault of a timing- @@ -263,10 +272,7 @@ bool LadspaEffectsModule::RegisterPlugin(PluginManagerInterface & pm, wxSetWorkingDirectory(saveOldCWD); hadpath ? wxSetEnv(wxT("PATH"), envpath) : wxUnsetEnv(wxT("PATH")); - if (error) - errMsg = _("Could not load the library"); - - return index > 0; + return nLoaded; } bool LadspaEffectsModule::IsPluginValid(const wxString & path, bool bFast) diff --git a/src/effects/ladspa/LadspaEffect.h b/src/effects/ladspa/LadspaEffect.h index 602da74e1..8e4918e10 100644 --- a/src/effects/ladspa/LadspaEffect.h +++ b/src/effects/ladspa/LadspaEffect.h @@ -223,10 +223,15 @@ public: bool Initialize() override; void Terminate() override; + bool PathsAreFiles() override { return true; } + wxString InstallPath() override; + bool AutoRegisterPlugins(PluginManagerInterface & pm) override; - wxArrayString FindPlugins(PluginManagerInterface & pm) override; - bool RegisterPlugin(PluginManagerInterface & pm, const wxString & path, - wxString &errMsg) override; + wxArrayString FindPluginPaths(PluginManagerInterface & pm) override; + unsigned DiscoverPluginsAtPath( + const wxString & path, wxString &errMsg, + const RegistrationCallback &callback) + override; bool IsPluginValid(const wxString & path, bool bFast) override; diff --git a/src/effects/lv2/LoadLV2.cpp b/src/effects/lv2/LoadLV2.cpp index f479be3b3..8f12a3cea 100644 --- a/src/effects/lv2/LoadLV2.cpp +++ b/src/effects/lv2/LoadLV2.cpp @@ -218,7 +218,7 @@ bool LV2EffectsModule::AutoRegisterPlugins(PluginManagerInterface & WXUNUSED(pm) return false; } -wxArrayString LV2EffectsModule::FindPlugins(PluginManagerInterface & WXUNUSED(pm)) +wxArrayString LV2EffectsModule::FindPluginPaths(PluginManagerInterface & WXUNUSED(pm)) { // Retrieve data about all LV2 plugins const LilvPlugins *plugs = lilv_world_get_all_plugins(gWorld); @@ -242,8 +242,9 @@ wxArrayString LV2EffectsModule::FindPlugins(PluginManagerInterface & WXUNUSED(pm return plugins; } -bool LV2EffectsModule::RegisterPlugin(PluginManagerInterface & pm, - const wxString & path, wxString &errMsg) +unsigned LV2EffectsModule::DiscoverPluginsAtPath( + const wxString & path, wxString &errMsg, + const RegistrationCallback &callback) { errMsg.clear(); const LilvPlugin *plug = GetPlugin(path); @@ -252,13 +253,13 @@ bool LV2EffectsModule::RegisterPlugin(PluginManagerInterface & pm, LV2Effect effect(plug); if (effect.SetHost(NULL)) { - pm.RegisterPlugin(this, &effect); - return true; + callback( this, &effect ); + return 1; } } errMsg = _("Could not load the library"); - return false; + return 0; } bool LV2EffectsModule::IsPluginValid(const wxString & path, bool bFast) diff --git a/src/effects/lv2/LoadLV2.h b/src/effects/lv2/LoadLV2.h index ea9da07dd..b89bdab1a 100644 --- a/src/effects/lv2/LoadLV2.h +++ b/src/effects/lv2/LoadLV2.h @@ -88,10 +88,15 @@ public: bool Initialize() override; void Terminate() override; + bool PathsAreFiles() override { return false; } + wxString InstallPath() override { return {}; } + bool AutoRegisterPlugins(PluginManagerInterface & pm) override; - wxArrayString FindPlugins(PluginManagerInterface & pm) override; - bool RegisterPlugin(PluginManagerInterface & pm, - const wxString & path, wxString &errMsg) override; + wxArrayString FindPluginPaths(PluginManagerInterface & pm) override; + unsigned DiscoverPluginsAtPath( + const wxString & path, wxString &errMsg, + const RegistrationCallback &callback) + override; bool IsPluginValid(const wxString & path, bool bFast) override; diff --git a/src/effects/nyquist/LoadNyquist.cpp b/src/effects/nyquist/LoadNyquist.cpp index 00e6ca5aa..c60226f18 100644 --- a/src/effects/nyquist/LoadNyquist.cpp +++ b/src/effects/nyquist/LoadNyquist.cpp @@ -15,6 +15,7 @@ #include "Nyquist.h" #include "LoadNyquist.h" +#include "../../FileNames.h" // ============================================================================ // List of effects that ship with Audacity. These will be autoregistered. @@ -159,6 +160,11 @@ void NyquistEffectsModule::Terminate() return; } +wxString NyquistEffectsModule::InstallPath() +{ + return FileNames::PlugInDir(); +} + bool NyquistEffectsModule::AutoRegisterPlugins(PluginManagerInterface & pm) { // Autoregister effects that we "think" are ones that have been shipped with @@ -170,7 +176,8 @@ bool NyquistEffectsModule::AutoRegisterPlugins(PluginManagerInterface & pm) if (!pm.IsPluginRegistered(NYQUIST_PROMPT_ID)) { // No checking of error ? - RegisterPlugin(pm, NYQUIST_PROMPT_ID, ignoredErrMsg); + DiscoverPluginsAtPath(NYQUIST_PROMPT_ID, ignoredErrMsg, + PluginManagerInterface::DefaultRegistrationCallback); } for (size_t i = 0; i < WXSIZEOF(kShippedEffects); i++) @@ -182,7 +189,8 @@ bool NyquistEffectsModule::AutoRegisterPlugins(PluginManagerInterface & pm) if (!pm.IsPluginRegistered(files[j])) { // No checking of error ? - RegisterPlugin(pm, files[j], ignoredErrMsg); + DiscoverPluginsAtPath(files[j], ignoredErrMsg, + PluginManagerInterface::DefaultRegistrationCallback); } } } @@ -191,7 +199,7 @@ bool NyquistEffectsModule::AutoRegisterPlugins(PluginManagerInterface & pm) return false; } -wxArrayString NyquistEffectsModule::FindPlugins(PluginManagerInterface & pm) +wxArrayString NyquistEffectsModule::FindPluginPaths(PluginManagerInterface & pm) { wxArrayString pathList = NyquistEffect::GetNyquistSearchPath(); wxArrayString files; @@ -207,20 +215,20 @@ wxArrayString NyquistEffectsModule::FindPlugins(PluginManagerInterface & pm) return files; } -bool NyquistEffectsModule::RegisterPlugin(PluginManagerInterface & pm, - const wxString & path, - wxString &errMsg) +unsigned NyquistEffectsModule::DiscoverPluginsAtPath( + const wxString & path, wxString &errMsg, + const RegistrationCallback &callback) { errMsg.clear(); NyquistEffect effect(path); if (effect.IsOk()) { - pm.RegisterPlugin(this, &effect); - return true; + callback(this, &effect); + return 1; } errMsg = effect.InitializationError(); - return false; + return 0; } bool NyquistEffectsModule::IsPluginValid(const wxString & path, bool bFast) diff --git a/src/effects/nyquist/LoadNyquist.h b/src/effects/nyquist/LoadNyquist.h index 81da38c1b..5d208bacb 100644 --- a/src/effects/nyquist/LoadNyquist.h +++ b/src/effects/nyquist/LoadNyquist.h @@ -38,10 +38,15 @@ public: bool Initialize() override; void Terminate() override; + bool PathsAreFiles() override { return true; } + wxString InstallPath() override; + bool AutoRegisterPlugins(PluginManagerInterface & pm) override; - wxArrayString FindPlugins(PluginManagerInterface & pm) override; - bool RegisterPlugin(PluginManagerInterface & pm, - const wxString & path, wxString &errMsg) override; + wxArrayString FindPluginPaths(PluginManagerInterface & pm) override; + unsigned DiscoverPluginsAtPath( + const wxString & path, wxString &errMsg, + const RegistrationCallback &callback) + override; bool IsPluginValid(const wxString & path, bool bFast) override; diff --git a/src/effects/vamp/LoadVamp.cpp b/src/effects/vamp/LoadVamp.cpp index 12fe5f7bc..6e2ed2058 100644 --- a/src/effects/vamp/LoadVamp.cpp +++ b/src/effects/vamp/LoadVamp.cpp @@ -122,7 +122,7 @@ bool VampEffectsModule::AutoRegisterPlugins(PluginManagerInterface & WXUNUSED(pm return false; } -wxArrayString VampEffectsModule::FindPlugins(PluginManagerInterface & WXUNUSED(pm)) +wxArrayString VampEffectsModule::FindPluginPaths(PluginManagerInterface & WXUNUSED(pm)) { wxArrayString names; @@ -199,8 +199,9 @@ wxArrayString VampEffectsModule::FindPlugins(PluginManagerInterface & WXUNUSED(p return names; } -bool VampEffectsModule::RegisterPlugin(PluginManagerInterface & pm, - const wxString & path, wxString &errMsg) +unsigned VampEffectsModule::DiscoverPluginsAtPath( + const wxString & path, wxString &errMsg, + const RegistrationCallback &callback) { errMsg.clear(); int output; @@ -210,13 +211,13 @@ bool VampEffectsModule::RegisterPlugin(PluginManagerInterface & pm, if (vp) { VampEffect effect(std::move(vp), path, output, hasParameters); - pm.RegisterPlugin(this, &effect); + callback( this, &effect ); - return true; + return 1; } errMsg = _("Could not load the library"); - return false; + return 0; } bool VampEffectsModule::IsPluginValid(const wxString & path, bool bFast) diff --git a/src/effects/vamp/LoadVamp.h b/src/effects/vamp/LoadVamp.h index f24fd8fe5..f7025751c 100644 --- a/src/effects/vamp/LoadVamp.h +++ b/src/effects/vamp/LoadVamp.h @@ -42,10 +42,15 @@ public: bool Initialize() override; void Terminate() override; + bool PathsAreFiles() override { return false; } + wxString InstallPath() override { return {}; } + bool AutoRegisterPlugins(PluginManagerInterface & pm) override; - wxArrayString FindPlugins(PluginManagerInterface & pm) override; - bool RegisterPlugin(PluginManagerInterface & pm, - const wxString & path, wxString &errMsg) override; + wxArrayString FindPluginPaths(PluginManagerInterface & pm) override; + unsigned DiscoverPluginsAtPath( + const wxString & path, wxString &errMsg, + const RegistrationCallback &callback) + override; bool IsPluginValid(const wxString & path, bool bFast) override; From 2c2db0fe09d01d66b535656471749e4903f3d5f6 Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Thu, 28 Dec 2017 19:15:26 -0500 Subject: [PATCH 3/3] Implement drag-and-drop of Nyquist, LADSPA, VST plug-ins... ... I'm not sure how to make it work for AudioUnits, LV2, or Vamp, for which the API for identifying a plug-in doesn't map straightforwardly to a file name. --- src/Experimental.h | 4 ++ src/PluginManager.cpp | 105 ++++++++++++++++++++++++++++++++++++++++++ src/PluginManager.h | 2 + src/Project.cpp | 11 +++++ 4 files changed, 122 insertions(+) diff --git a/src/Experimental.h b/src/Experimental.h index ad135bd6b..7c195482b 100644 --- a/src/Experimental.h +++ b/src/Experimental.h @@ -224,6 +224,10 @@ // scrolling past zero is enabled. Perhaps that lessens confusion. #define EXPERIMENTAL_TWO_TONE_TIME_RULER +// Paul Licameli (PRL) 28 Dec 2017 +// Easy drag-and-drop to add Nyquist, LADSPA, and VST plug-ins +#define EXPERIMENTAL_DRAG_DROP_PLUG_INS + #ifndef IN_RC // Define to include crash reporting #include diff --git a/src/PluginManager.cpp b/src/PluginManager.cpp index 930412531..d92c20c91 100644 --- a/src/PluginManager.cpp +++ b/src/PluginManager.cpp @@ -1747,6 +1747,111 @@ void PluginManager::Terminate() } } +bool PluginManager::DropFile(const wxString &fileName) +{ + auto &mm = ModuleManager::Get(); + + for (const PluginDescriptor *plug = GetFirstPlugin(PluginTypeModule); + plug; + plug = GetNextPlugin(PluginTypeModule)) + { + auto module = static_cast + (mm.CreateProviderInstance(plug->GetID(), plug->GetPath())); + const auto &ff = module->InstallPath(); + if (!ff.empty() && module->PathsAreFiles()) { + wxString errMsg; + // Do dry-run test of the file format + unsigned nPlugIns = module->DiscoverPluginsAtPath(fileName, errMsg, + [](ModuleInterface *, EffectIdentInterface *){}); + if (nPlugIns) { + // File contents are good for this module, so check no others. + // All branches of this block return true, even in case of + // failure for other reasons, to signal that other drag-and-drop + // actions should not be tried. + + // Find path to copy it + const wxFileName src{ fileName }; + wxFileName dst; + dst.AssignDir( ff ); + dst.SetFullName( src.GetFullName() ); + if ( dst.Exists() ) { + // Query whether to overwrite + bool overwrite = (wxYES == ::wxMessageBox( + wxString::Format(_("Overwrite the plug-in file %s ?"), + dst.GetFullPath() ), + _("Plug-in already exists"), + wxYES_NO + ) ); + if ( !overwrite ) + return true; + } + + // Move the file or subtree + bool copied = false; + auto dstPath = dst.GetFullPath(); + if ( src.FileExists() ) + // A simple one-file plug-in + copied = FileNames::CopyFile( + src.GetFullPath(), dstPath, true ); + else { + // A sub-folder + // such as for some VST packages + // Recursive copy needed -- to do + return true; + } + + if (!copied) { + ::wxMessageBox( + _("Plug-in file is in use. Failed to overwrite")); + return true; + } + + // Register for real + std::vector ids; + std::vector names; + nPlugIns = module->DiscoverPluginsAtPath(dstPath, errMsg, + [&](ModuleInterface *provider, EffectIdentInterface *ident){ + // Register as by default, but also collecting the PluginIDs + // and names + ids.push_back( + PluginManagerInterface::DefaultRegistrationCallback( + provider, ident) ); + names.push_back( wxGetTranslation( ident->GetName() ) ); + }); + if ( ! nPlugIns ) { + // Unlikely after the dry run succeeded + ::wxMessageBox( wxString::Format( + _("Failed to register:\n%s"), errMsg ) ); + return true; + } + + // Ask whether to enable the plug-ins + if (auto nIds = ids.size()) { + auto format = wxPLURAL( + _("Enable this plug-in?"), + _("Enable these plug-ins?"), + nIds + ); + format += wxT("\n"); + for (const auto &name : names) + format += name + wxT("\n"); + bool enable = (wxYES == ::wxMessageBox( + wxString::Format( format, nIds ), + _("Enable new plug-ins"), + wxYES_NO + ) ); + for (const auto &id : ids) + mPlugins[id].SetEnabled(enable); + } + + return true; + } + } + } + + return false; +} + void PluginManager::Load() { // Create/Open the registry diff --git a/src/PluginManager.h b/src/PluginManager.h index 1e93f0a67..a1c2dd4a3 100644 --- a/src/PluginManager.h +++ b/src/PluginManager.h @@ -228,6 +228,8 @@ public: void Initialize(); void Terminate(); + bool DropFile(const wxString &fileName); + static PluginManager & Get(); static PluginID GetID(ModuleInterface *module); diff --git a/src/Project.cpp b/src/Project.cpp index 829b38caa..6c3dbd1a1 100644 --- a/src/Project.cpp +++ b/src/Project.cpp @@ -461,6 +461,17 @@ public: } ); for (const auto &name : sortednames) { + +#ifdef EXPERIMENTAL_DRAG_DROP_PLUG_INS + // Is it a plug-in? + if (PluginManager::Get().DropFile(name)) { + mProject->RebuildAllMenuBars(); + continue; + } + + // No, so import. +#endif + if (Importer::IsMidi(name)) AudacityProject::DoImportMIDI(mProject, name); else