mirror of
https://github.com/robbert-vdh/yabridge.git
synced 2026-06-20 02:13:56 +02:00
Interact with CLAP bridge in libyabridge-clap.so
This commit is contained in:
+117
-5
@@ -18,6 +18,28 @@
|
|||||||
|
|
||||||
#include <clap/entry.h>
|
#include <clap/entry.h>
|
||||||
|
|
||||||
|
#include "bridges/clap.h"
|
||||||
|
|
||||||
|
using namespace std::literals::string_literals;
|
||||||
|
|
||||||
|
namespace fs = ghc::filesystem;
|
||||||
|
|
||||||
|
// 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 number of active instances. Incremented when `clap_entry_init()` is
|
* The number of active instances. Incremented when `clap_entry_init()` is
|
||||||
* called, decremented when `clap_entry_exit()` is called. We'll initialize the
|
* called, decremented when `clap_entry_exit()` is called. We'll initialize the
|
||||||
@@ -25,12 +47,49 @@
|
|||||||
* when a `clap_entry_exit()` call causes this to return back to 0.
|
* when a `clap_entry_exit()` call causes this to return back to 0.
|
||||||
*/
|
*/
|
||||||
std::atomic_size_t active_instances = 0;
|
std::atomic_size_t active_instances = 0;
|
||||||
|
/**
|
||||||
|
* 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<ClapPluginBridge> bridge;
|
||||||
|
|
||||||
bool clap_entry_init(const char* plugin_path) {
|
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 since most people likely won't see the
|
||||||
|
// above message
|
||||||
|
send_notification(
|
||||||
|
"Failed to initialize CLAP plugin",
|
||||||
|
error.what() +
|
||||||
|
"\nCheck the plugin's output in a terminal for more information"s,
|
||||||
|
plugin_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool clap_entry_init(const char* /*plugin_path*/) {
|
||||||
// This function can be called multiple times, so we should make sure to
|
// This function can be called multiple times, so we should make sure to
|
||||||
// only initialize the bridge on the first call
|
// only initialize the bridge on the first call
|
||||||
if (active_instances.fetch_add(1, std::memory_order_seq_cst) == 0) {
|
if (active_instances.fetch_add(1, std::memory_order_seq_cst) == 0) {
|
||||||
// TODO: Increase reference count, init factory if it was zero
|
assert(!bridge);
|
||||||
|
|
||||||
|
// XXX: The host also provides us with the plugin path which we could
|
||||||
|
// just use instead. Should we? The advantage of doing it this way
|
||||||
|
// instead is that we'll have consistent behavior between all
|
||||||
|
// plugin formats.
|
||||||
|
const fs::path plugin_path = get_this_file_location();
|
||||||
|
try {
|
||||||
|
bridge = std::make_unique<ClapPluginBridge>(plugin_path);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (const std::exception& error) {
|
||||||
|
log_init_error(error, plugin_path);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -40,18 +99,71 @@ void clap_entry_deinit() {
|
|||||||
// We'll free the bridge when this exits brings the reference count back to
|
// We'll free the bridge when this exits brings the reference count back to
|
||||||
// zero
|
// zero
|
||||||
if (active_instances.fetch_sub(1, std::memory_order_seq_cst) == 1) {
|
if (active_instances.fetch_sub(1, std::memory_order_seq_cst) == 1) {
|
||||||
// TODO: Destroy the bridge instance
|
assert(bridge);
|
||||||
|
|
||||||
|
bridge.reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const void* clap_entry_get_factory(const char* factory_id) {
|
const void* clap_entry_get_factory(const char* factory_id) {
|
||||||
// TODO: Do the thing
|
assert(bridge);
|
||||||
// TODO: Assertion
|
assert(factory_id);
|
||||||
|
|
||||||
|
return bridge->get_factory(factory_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This visibility attribute doesn't do anything on data with external linkage,
|
||||||
|
// but we'll include it here just because it's in the CLAP template
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Wattributes"
|
||||||
|
|
||||||
CLAP_EXPORT const clap_plugin_entry_t clap_entry = {
|
CLAP_EXPORT const clap_plugin_entry_t clap_entry = {
|
||||||
.clap_version = CLAP_VERSION_INIT,
|
.clap_version = CLAP_VERSION_INIT,
|
||||||
.init = clap_entry_init,
|
.init = clap_entry_init,
|
||||||
.deinit = clap_entry_deinit,
|
.deinit = clap_entry_deinit,
|
||||||
.get_factory = clap_entry_get_factory,
|
.get_factory = clap_entry_get_factory,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 ClapPluginBridge* yabridge_module_init(
|
||||||
|
const char* plugin_path) {
|
||||||
|
assert(plugin_path);
|
||||||
|
|
||||||
|
try {
|
||||||
|
return new ClapPluginBridge(plugin_path);
|
||||||
|
} catch (const std::exception& error) {
|
||||||
|
log_init_error(error, plugin_path);
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Free a bridge instance returned by `yabridge_module_init`.
|
||||||
|
*/
|
||||||
|
extern "C" YABRIDGE_EXPORT void yabridge_module_free(
|
||||||
|
ClapPluginBridge* instance) {
|
||||||
|
if (instance) {
|
||||||
|
delete instance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create and return a factory from a bridge instance. Used by the chainloaders.
|
||||||
|
*/
|
||||||
|
extern "C" YABRIDGE_EXPORT const void* yabridge_module_get_factory(
|
||||||
|
ClapPluginBridge* instance,
|
||||||
|
const char* factory_id) {
|
||||||
|
assert(instance);
|
||||||
|
assert(factory_id);
|
||||||
|
|
||||||
|
return instance->get_factory(factory_id);
|
||||||
|
}
|
||||||
|
|||||||
@@ -68,6 +68,20 @@ vst2_plugin_sources = files(
|
|||||||
|
|
||||||
if with_clap
|
if with_clap
|
||||||
clap_plugin_sources = files(
|
clap_plugin_sources = files(
|
||||||
|
'../common/communication/common.cpp',
|
||||||
|
'../common/configuration.cpp',
|
||||||
|
'../common/logging/clap.cpp',
|
||||||
|
'../common/logging/common.cpp',
|
||||||
|
'../common/audio-shm.cpp',
|
||||||
|
'../common/linking.cpp',
|
||||||
|
'../common/notifications.cpp',
|
||||||
|
'../common/plugins.cpp',
|
||||||
|
'../common/process.cpp',
|
||||||
|
'../common/utils.cpp',
|
||||||
|
'../include/llvm/small-vector.cpp',
|
||||||
|
'bridges/clap.cpp',
|
||||||
|
'host-process.cpp',
|
||||||
|
'utils.cpp',
|
||||||
'clap-plugin.cpp',
|
'clap-plugin.cpp',
|
||||||
)
|
)
|
||||||
endif
|
endif
|
||||||
|
|||||||
Reference in New Issue
Block a user