mirror of
https://github.com/robbert-vdh/yabridge.git
synced 2026-05-07 03:50:11 +02:00
Export chainloader functions in the plugin libs
These can be called from the new chainlaoder libraries to use yabridge
without needing copies of the full fat `libyabridge-{vst2,vst3}.so`
libraries.
This commit is contained in:
@@ -385,6 +385,8 @@ class PluginBridge {
|
||||
|
||||
// Also show a desktop notification so users running from
|
||||
// the GUI get a heads up
|
||||
// FIXME: Go through these messages and update them to
|
||||
// reflect the chainloading changes
|
||||
send_notification(
|
||||
"Failed to start the Wine plugin host",
|
||||
"Check yabridge's output for more information on what "
|
||||
@@ -423,6 +425,8 @@ class PluginBridge {
|
||||
" you may need rerun 'yabridgectl sync' first to");
|
||||
generic_logger_.log(" update your plugins.");
|
||||
|
||||
// FIXME: Go through these messages and update them to reflect the
|
||||
// chainloading changes
|
||||
send_notification(
|
||||
"Version mismatch",
|
||||
"If you just updated yabridge, then you may need "
|
||||
|
||||
+65
-24
@@ -16,11 +16,7 @@
|
||||
|
||||
#include <vestige/aeffectx.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
|
||||
#include "../common/linking.h"
|
||||
#include "../common/logging/common.h"
|
||||
#include "bridges/vst2.h"
|
||||
|
||||
using namespace std::literals::string_literals;
|
||||
@@ -32,10 +28,45 @@ namespace fs = ghc::filesystem;
|
||||
// (EnergyXT being the only known host on Linux that uses the `main` entry
|
||||
// point).
|
||||
|
||||
// These plugin libraries can be used in one of two ways: they can either be
|
||||
// loaded directly (the yabridge <4.0 way), or they can be loaded indirectly
|
||||
// from `yabridge-chainloader-*.so` (the yabridge >=4.0 way). The advantage of
|
||||
// chainloading this library from a tiny stub library is that yabridge can be
|
||||
// updated without having to also replace all of the library copies and that it
|
||||
// takes up less space on filesystems that don't support reflinking, but the
|
||||
// catch is that we no longer have one unique plugin bridge library per plugin.
|
||||
// This means that we cannot store the current bridge instance as a global in
|
||||
// this library (because it would then be shared by multiple chainloaders), and
|
||||
// that we cannot use `dladdr()` within this library to get the path to the
|
||||
// current plugin, because thatq would return the path to this shared plugin
|
||||
// library instead. To accommodate for this, we'll provide the usual plugin
|
||||
// entry points, and we'll also provide simple methods for initializing the
|
||||
// bridge so that the chainloading library can hold on to the bridge instance
|
||||
// instead of this library.
|
||||
|
||||
void log_init_error(const std::exception& error, const fs::path& plugin_path) {
|
||||
Logger logger = Logger::create_exception_logger();
|
||||
|
||||
logger.log("");
|
||||
logger.log("Error during initialization:");
|
||||
logger.log(error.what());
|
||||
logger.log("");
|
||||
|
||||
// Also show a desktop notification most people likely won't see the above
|
||||
// message
|
||||
send_notification(
|
||||
"Failed to initialize VST2 plugin",
|
||||
error.what() +
|
||||
"\nIf you just updated yabridge, then you may need to rerun "
|
||||
"'yabridgectl sync' first to update your plugins."s,
|
||||
plugin_path);
|
||||
}
|
||||
|
||||
/**
|
||||
* The main VST2 plugin entry point. We first set up a bridge that connects to a
|
||||
* Wine process that hosts the Windows VST2 plugin. We then create and return a
|
||||
* VST plugin struct that acts as a passthrough to the bridge.
|
||||
* The main VST2 plugin entry point for when this plugin library is used
|
||||
* directly. We first set up a bridge that connects to a Wine process that hosts
|
||||
* the Windows VST2 plugin. We then create and return a VST plugin struct that
|
||||
* acts as a passthrough to the bridge.
|
||||
*
|
||||
* To keep this somewhat contained this is the only place where we're doing
|
||||
* manual memory management. Clean up is done when we receive the `effClose`
|
||||
@@ -43,9 +74,9 @@ namespace fs = ghc::filesystem;
|
||||
*/
|
||||
extern "C" YABRIDGE_EXPORT AEffect* VSTPluginMain(
|
||||
audioMasterCallback host_callback) {
|
||||
// FIXME: Update this for the chainloading
|
||||
const fs::path plugin_path = get_this_file_location();
|
||||
assert(host_callback);
|
||||
|
||||
const fs::path plugin_path = get_this_file_location();
|
||||
try {
|
||||
// This is the only place where we have to use manual memory management.
|
||||
// The bridge's destructor is called when the `effClose` opcode is
|
||||
@@ -55,21 +86,7 @@ extern "C" YABRIDGE_EXPORT AEffect* VSTPluginMain(
|
||||
|
||||
return &bridge->plugin_;
|
||||
} catch (const std::exception& error) {
|
||||
Logger logger = Logger::create_exception_logger();
|
||||
|
||||
logger.log("");
|
||||
logger.log("Error during initialization:");
|
||||
logger.log(error.what());
|
||||
logger.log("");
|
||||
|
||||
// Also show a desktop notification most people likely won't see the
|
||||
// above message
|
||||
send_notification(
|
||||
"Failed to initialize VST2 plugin",
|
||||
error.what() +
|
||||
"\nIf you just updated yabridge, then you may need to rerun "
|
||||
"'yabridgectl sync' first to update your plugins."s,
|
||||
plugin_path);
|
||||
log_init_error(error, plugin_path);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
@@ -83,3 +100,27 @@ extern "C" YABRIDGE_EXPORT AEffect* deprecated_main(
|
||||
YABRIDGE_EXPORT AEffect* deprecated_main(audioMasterCallback audioMaster) {
|
||||
return VSTPluginMain(audioMaster);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function can be called from the chainloader to initialize a new plugin
|
||||
* bridge instance. Since VST2 only has a single plugin entry point and plugins
|
||||
* clean up after themselves in `effClose()`, this is the only function the
|
||||
* chainloader will call.
|
||||
*/
|
||||
extern "C" YABRIDGE_EXPORT AEffect* yabridge_plugin_init(
|
||||
audioMasterCallback host_callback,
|
||||
const char* plugin_path) {
|
||||
assert(host_callback);
|
||||
assert(plugin_path);
|
||||
|
||||
try {
|
||||
Vst2PluginBridge* bridge =
|
||||
new Vst2PluginBridge(plugin_path, host_callback);
|
||||
|
||||
return &bridge->plugin_;
|
||||
} catch (const std::exception& error) {
|
||||
log_init_error(error, plugin_path);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
+90
-21
@@ -14,6 +14,9 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include "../common/linking.h"
|
||||
#include "bridges/vst3.h"
|
||||
|
||||
// FIXME: The VST3 SDK as of version 3.7.2 now includes multiple local functions
|
||||
// called `InitModule` and `DeinitModule`: one in the new
|
||||
// `public.sdk/source/main/initmodule.cpp`, and the existing ones in the
|
||||
@@ -25,9 +28,6 @@
|
||||
// NOLINTNEXTLINE(bugprone-suspicious-include)
|
||||
#include <public.sdk/source/main/linuxmain.cpp>
|
||||
|
||||
#include "../common/linking.h"
|
||||
#include "bridges/vst3.h"
|
||||
|
||||
using namespace std::literals::string_literals;
|
||||
|
||||
namespace fs = ghc::filesystem;
|
||||
@@ -46,36 +46,63 @@ namespace fs = ghc::filesystem;
|
||||
// plugin symlinked to either the `x86_64-win` or the `x86-win` directory inside
|
||||
// of the bundle, even if it does not come in a bundle itself.
|
||||
|
||||
// These plugin libraries can be used in one of two ways: they can either be
|
||||
// loaded directly (the yabridge <4.0 way), or they can be loaded indirectly
|
||||
// from `yabridge-chainloader-*.so` (the yabridge >=4.0 way). The advantage of
|
||||
// chainloading this library from a tiny stub library is that yabridge can be
|
||||
// updated without having to also replace all of the library copies and that it
|
||||
// takes up less space on filesystems that don't support reflinking, but the
|
||||
// catch is that we no longer have one unique plugin bridge library per plugin.
|
||||
// This means that we cannot store the current bridge instance as a global in
|
||||
// this library (because it would then be shared by multiple chainloaders), and
|
||||
// that we cannot use `dladdr()` within this library to get the path to the
|
||||
// current plugin, because thatq would return the path to this shared plugin
|
||||
// library instead. To accommodate for this, we'll provide the usual plugin
|
||||
// entry points, and we'll also provide simple methods for initializing the
|
||||
// bridge so that the chainloading library can hold on to the bridge instance
|
||||
// instead of this library.
|
||||
|
||||
/**
|
||||
* The global plugin bridge instance. Only used if this plugin library is used
|
||||
* directly. When the library is chainloaded, this will remain a null pointer.
|
||||
*/
|
||||
std::unique_ptr<Vst3PluginBridge> bridge;
|
||||
|
||||
void log_init_exception(const std::exception& error,
|
||||
const fs::path& plugin_path) {
|
||||
Logger logger = Logger::create_exception_logger();
|
||||
|
||||
logger.log("");
|
||||
logger.log("Error during initialization:");
|
||||
logger.log(error.what());
|
||||
logger.log("");
|
||||
|
||||
// Also show a desktop notification most people likely won't see the above
|
||||
// message
|
||||
// FIXME: Go through these messages and update them to reflect the
|
||||
// chainloading changes
|
||||
send_notification(
|
||||
"Failed to initialize VST3 plugin",
|
||||
error.what() +
|
||||
"\nIf you just updated yabridge, then you may need to rerun "
|
||||
"'yabridgectl sync' first to update your plugins."s,
|
||||
plugin_path);
|
||||
}
|
||||
|
||||
// These functions are called by the `ModuleEntry` and `ModuleExit` functions on
|
||||
// the first load and load unload
|
||||
// the first load and load unload. The chainloader library has similar functions
|
||||
// that call the `yabridge_module_` functions exported at the bottom of
|
||||
// this file.
|
||||
bool InitModule() {
|
||||
assert(!bridge);
|
||||
|
||||
// FIXME: Update this for the chainloading
|
||||
const fs::path plugin_path = get_this_file_location();
|
||||
|
||||
try {
|
||||
bridge = std::make_unique<Vst3PluginBridge>(plugin_path);
|
||||
|
||||
return true;
|
||||
} catch (const std::exception& error) {
|
||||
Logger logger = Logger::create_exception_logger();
|
||||
|
||||
logger.log("");
|
||||
logger.log("Error during initialization:");
|
||||
logger.log(error.what());
|
||||
logger.log("");
|
||||
|
||||
// Also show a desktop notification most people likely won't see the
|
||||
// above message
|
||||
send_notification(
|
||||
"Failed to initialize VST3 plugin",
|
||||
error.what() +
|
||||
"\nIf you just updated yabridge, then you may need to rerun "
|
||||
"'yabridgectl sync' first to update your plugins."s,
|
||||
plugin_path);
|
||||
log_init_exception(error, plugin_path);
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -101,3 +128,45 @@ GetPluginFactory() {
|
||||
|
||||
return bridge->get_plugin_factory();
|
||||
}
|
||||
|
||||
/**
|
||||
* This function can be called from the chainloader to initialize a new plugin
|
||||
* bridge instance. The caller should store the pointer and later free it again
|
||||
* using the `yabridge_module_free()` function. If the bridge could not
|
||||
* initialize due to an error, then the error will be logged and a null pointer
|
||||
* will be returned.
|
||||
*/
|
||||
extern "C" YABRIDGE_EXPORT Vst3PluginBridge* yabridge_module_init(
|
||||
const char* plugin_path) {
|
||||
assert(plugin_path);
|
||||
|
||||
try {
|
||||
return new Vst3PluginBridge(plugin_path);
|
||||
} catch (const std::exception& error) {
|
||||
log_init_exception(error, plugin_path);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Free a bridge instance returned by `yabridge_module_init`.
|
||||
*/
|
||||
extern "C" YABRIDGE_EXPORT void yabridge_module_free(
|
||||
Vst3PluginBridge* instance) {
|
||||
if (instance) {
|
||||
delete instance;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Our VST3 plugin's entry point. When building the plugin factory we'll host
|
||||
* the plugin in our Wine application, retrieve its information and supported
|
||||
* classes, and then recreate it here.
|
||||
*/
|
||||
extern "C" YABRIDGE_EXPORT Steinberg::IPluginFactory*
|
||||
yabridge_module_get_plugin_factory(Vst3PluginBridge* instance) {
|
||||
assert(instance);
|
||||
|
||||
return instance->get_plugin_factory();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user