diff --git a/libs/ardour/ardour/vst3_module.h b/libs/ardour/ardour/vst3_module.h new file mode 100644 index 0000000000..b88f58d250 --- /dev/null +++ b/libs/ardour/ardour/vst3_module.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2019 Robin Gareus + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef _ardour_vst3_module_h_ +#define _ardour_vst3_module_h_ + +#include + +#include "ardour/libardour_visibility.h" + +namespace ARDOUR { + +class LIBARDOUR_API VST3PluginModule +{ +public: + static boost::shared_ptr load (std::string const& path); + VST3PluginModule () {} + virtual ~VST3PluginModule () {} + + virtual void* fn_ptr (const char* name) const = 0; + +protected: + virtual bool init () = 0; + virtual bool exit () = 0; + + /* prevent copy construction */ + VST3PluginModule (VST3PluginModule const&); +}; + +} // namespace ARDOUR +#endif diff --git a/libs/ardour/plugin_manager.cc b/libs/ardour/plugin_manager.cc index 6c376664ce..ae31c7e927 100644 --- a/libs/ardour/plugin_manager.cc +++ b/libs/ardour/plugin_manager.cc @@ -115,6 +115,8 @@ #endif #ifdef VST3_SUPPORT +#include "pbd/basename.h" +#include "ardour/vst3_module.h" #include "ardour/vst3_plugin.h" #endif @@ -1532,7 +1534,7 @@ PluginManager::vst3_discover (string const& path, bool cache_only) module_path = path; } else { module_path = Glib::build_filename (path, "Contents", - vst3_bindir (), basename_nosuffix (path) + vst3_suffix ()); + vst3_bindir (), PBD::basename_nosuffix (path) + vst3_suffix ()); } if (!Glib::file_test (module_path, Glib::FILE_TEST_IS_REGULAR)) { cerr << "VST3 not a valid bundle: '" << module_path << "'\n"; @@ -1541,6 +1543,13 @@ PluginManager::vst3_discover (string const& path, bool cache_only) ARDOUR::PluginScanMessage(_("VST3"), module_path, !(cache_only || cancelled())); DEBUG_TRACE (DEBUG::PluginManager, string_compose ("VST3: discover %1 (%2)\n", path, module_path)); + try { + boost::shared_ptr m = VST3PluginModule::load (module_path); + } catch (...) { + DEBUG_TRACE (DEBUG::PluginManager, string_compose ("Cannot load VST3 at '%1'\n", path)); + return -1; + } + return 0; } diff --git a/libs/ardour/vst3_module.cc b/libs/ardour/vst3_module.cc new file mode 100644 index 0000000000..584e776c39 --- /dev/null +++ b/libs/ardour/vst3_module.cc @@ -0,0 +1,230 @@ +/* + * Copyright (C) 2019 Robin Gareus + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifdef __APPLE__ +#include +#elif defined PLATFORM_WINDOWS +#include +#include +#else +#include +#endif + +#include + +#include "pbd/compose.h" +#include "pbd/error.h" +#include "pbd/failed_constructor.h" + +#include "ardour/vst3_module.h" + +#include "pbd/i18n.h" + +using namespace ARDOUR; + +#ifdef __APPLE__ + +class VST3MacModule : public VST3PluginModule +{ +public: + VST3MacModule (std::string const& module_path) + { + std::string path = Glib::path_get_dirname (module_path); // MacOS + path = Glib::path_get_dirname (path); // Contents + path = Glib::path_get_dirname (path); // theVST.vst3 + CFURLRef url = CFURLCreateFromFileSystemRepresentation (0, (const UInt8*)path.c_str (), (CFIndex)path.length (), true); + if (url) { + _bundle = CFBundleCreate (kCFAllocatorDefault, url); + CFRelease (url); + } + + if (!_bundle) { + throw failed_constructor (); + } + + if (!CFBundleLoadExecutable (_bundle)) { + CFRelease (_bundle); + _bundle = 0; + throw failed_constructor (); + } + + if (!init ()) { + CFRelease (_bundle); + _bundle = 0; + throw failed_constructor (); + } + } + + ~VST3MacModule () + { + if (_bundle) { + exit (); + CFRelease (_bundle); + } + } + + void* fn_ptr (const char* name) const + { + CFStringRef str = CFStringCreateWithCString (NULL, name, kCFStringEncodingUTF8); + void* fn = CFBundleGetFunctionPointerForName (_bundle, str); + if (str) { + CFRelease (str); + } + return fn; + } + +private: + bool init () + { + typedef bool (*init_fn_t) (CFBundleRef); + init_fn_t fn = (init_fn_t)fn_ptr ("bundleEntry"); + return (fn && fn (_bundle)); + } + + bool exit () + { + typedef bool (*exit_fn_t) (); + exit_fn_t fn = (exit_fn_t)fn_ptr ("bundleExit"); + return (fn && fn ()); + } + + CFBundleRef _bundle; +}; + +#elif defined PLATFORM_WINDOWS + +class VST3WindowsModule : public VST3PluginModule +{ +public: + VST3WindowsModule (const std::string& path) + { + if ((_handle = LoadLibraryA (Glib::locale_from_utf8 (path).c_str ())) == 0) { + throw failed_constructor (); + } + + if (!init ()) { + FreeLibrary (_handle); + _handle = 0; + throw failed_constructor (); + } + } + + ~VST3WindowsModule () + { + if (_handle) { + exit (); + FreeLibrary (_handle); + } + } + + void* fn_ptr (const char* name) const + { + return (void*)GetProcAddress (_handle, name); + } + +private: + bool init () + { + typedef bool(__stdcall * init_fn_t) (); + init_fn_t fn = (init_fn_t)fn_ptr ("InitDll"); + return (!fn || fn ()); // init is optional + } + + bool exit () + { + typedef bool(__stdcall * exit_fn_t) (); + exit_fn_t fn = (exit_fn_t)fn_ptr ("ExitDll"); + return (!fn || fn ()); // exit is optional + } + + HMODULE _handle; +}; + +#else + +class VST3LinuxModule : public VST3PluginModule +{ +public: + VST3LinuxModule (std::string const& path) + { + if ((_dll = dlopen (path.c_str (), RTLD_LOCAL | RTLD_LAZY)) == 0) { + PBD::error << string_compose (_("Could not load VST3 plugin '%1': %2"), path, dlerror ()) << endmsg; + throw failed_constructor (); + } + + void* m_entry = dlsym (_dll, "ModuleEntry"); + void* m_exit = dlsym (_dll, "ModuleExit"); + + if (!m_entry || !m_exit) { + PBD::error << string_compose (_("Invalid VST3 plugin: '%1'"), path) << endmsg; + dlclose (_dll); + _dll = 0; + throw failed_constructor (); + } + + if (!init ()) { + dlclose (_dll); + _dll = 0; + throw failed_constructor (); + } + } + + ~VST3LinuxModule () + { + if (_dll) { + exit (); + dlclose (_dll); + } + } + + void* fn_ptr (const char* name) const + { + return dlsym (_dll, name); + } + +private: + bool init () + { + typedef bool (*init_fn_t) (void*); + init_fn_t fn = (init_fn_t)fn_ptr ("ModuleEntry"); + return (fn && fn (_dll)); + } + + bool exit () + { + typedef bool (*exit_fn_t) (); + exit_fn_t fn = (exit_fn_t)fn_ptr ("ModuleExit"); + return (fn && fn ()); + } + + void* _dll; +}; + +#endif + +boost::shared_ptr +VST3PluginModule::load (std::string const& path) +{ +#ifdef __APPLE__ + return boost::shared_ptr (new VST3MacModule (path)); +#elif defined PLATFORM_WINDOWS + return boost::shared_ptr (new VST3WindowsModule (path)); +#else + return boost::shared_ptr (new VST3LinuxModule (path)); +#endif +} diff --git a/libs/ardour/wscript b/libs/ardour/wscript index 278f3a385a..3e403a8578 100644 --- a/libs/ardour/wscript +++ b/libs/ardour/wscript @@ -460,7 +460,7 @@ def build(bld): obj.defines += [ 'MACVST_SUPPORT' ] if bld.is_defined('VST3_SUPPORT'): - obj.source += [ 'vst3_plugin.cc' ] + obj.source += [ 'vst3_plugin.cc', 'vst3_module.cc' ] obj.defines += [ 'VST3_SUPPORT' ] if bld.is_defined('HAVE_COREAUDIO'):