From 423534f373a887f6616caad5a12dee4a626718b6 Mon Sep 17 00:00:00 2001 From: Robbert van der Helm Date: Thu, 8 Sep 2022 00:11:04 +0200 Subject: [PATCH] Implement Wine side for creating plugin instances --- src/common/logging/clap.cpp | 26 ++++++- src/common/logging/clap.h | 5 +- src/common/serialization/clap.h | 6 +- src/wine-host/bridges/clap-impls/host-proxy.h | 4 ++ src/wine-host/bridges/clap.cpp | 67 +++++++++++++++---- src/wine-host/bridges/clap.h | 31 +++++++-- 6 files changed, 113 insertions(+), 26 deletions(-) diff --git a/src/common/logging/clap.cpp b/src/common/logging/clap.cpp index 07df7f6f..ff8d25ef 100644 --- a/src/common/logging/clap.cpp +++ b/src/common/logging/clap.cpp @@ -29,6 +29,15 @@ bool ClapLogger::log_request(bool is_host_plugin, }); } +bool ClapLogger::log_request(bool is_host_plugin, + const clap::plugin_factory::Create& request) { + return log_request_base(is_host_plugin, [&](auto& message) { + message << "clap_plugin_factory::create(host = , " + "plugin_id = \"" + << request.plugin_id << "\")"; + }); +} + bool ClapLogger::log_request(bool is_host_plugin, const WantsConfiguration&) { return log_request_base(is_host_plugin, [&](auto& message) { message << "Requesting "; @@ -40,10 +49,10 @@ bool ClapLogger::log_request(bool is_host_plugin, const WantsConfiguration&) { // }); // } -bool ClapLogger::log_response( +void ClapLogger::log_response( bool is_host_plugin, const clap::plugin_factory::ListResponse& response) { - return log_request_base(is_host_plugin, [&](auto& message) { + return log_response_base(is_host_plugin, [&](auto& message) { if (response.descriptors) { message << "size() << " plugin descriptors>"; @@ -53,6 +62,19 @@ bool ClapLogger::log_response( }); } +void ClapLogger::log_response( + bool is_host_plugin, + const clap::plugin_factory::CreateResponse& response) { + return log_response_base(is_host_plugin, [&](auto& message) { + if (response.instance_id) { + message << ""; + } else { + message << ""; + } + }); +} + void ClapLogger::log_response(bool is_host_plugin, const Configuration&) { log_response_base(is_host_plugin, [&](auto& message) { message << ""; }); diff --git a/src/common/logging/clap.h b/src/common/logging/clap.h index bd1903e2..e81979d8 100644 --- a/src/common/logging/clap.h +++ b/src/common/logging/clap.h @@ -46,6 +46,7 @@ class ClapLogger { // log message for the response together with the request. bool log_request(bool is_host_plugin, const clap::plugin_factory::List&); + bool log_request(bool is_host_plugin, const clap::plugin_factory::Create&); // TODO: Audio thread requests // bool log_request(bool is_host_plugin, @@ -54,8 +55,10 @@ class ClapLogger { bool log_request(bool is_host_plugin, const WantsConfiguration&); // void log_response(bool is_host_plugin, const Ack&); - bool log_response(bool is_host_plugin, + void log_response(bool is_host_plugin, const clap::plugin_factory::ListResponse&); + void log_response(bool is_host_plugin, + const clap::plugin_factory::CreateResponse&); // TODO: Audio thread responses // void log_response(bool is_host_plugin, diff --git a/src/common/serialization/clap.h b/src/common/serialization/clap.h index 8cf67596..7c0cb98b 100644 --- a/src/common/serialization/clap.h +++ b/src/common/serialization/clap.h @@ -22,6 +22,7 @@ #include "../bitsery/ext/message-reference.h" #include "../utils.h" +#include "clap/host.h" #include "clap/plugin-factory.h" #include "common.h" @@ -38,8 +39,9 @@ */ // FIXME: Remove the `WantsConfiguration`. For some reason bitsery just won't // serialize this without it. -using ClapMainThreadControlRequest = - std::variant; +using ClapMainThreadControlRequest = std::variant; template void serialize(S& s, ClapMainThreadControlRequest& payload) { diff --git a/src/wine-host/bridges/clap-impls/host-proxy.h b/src/wine-host/bridges/clap-impls/host-proxy.h index 1324c8bd..0bc3c5d5 100644 --- a/src/wine-host/bridges/clap-impls/host-proxy.h +++ b/src/wine-host/bridges/clap-impls/host-proxy.h @@ -49,6 +49,10 @@ class clap_host_proxy { * Get a `clap_host` vtable that can be passed to the plugin. */ inline const clap_host_t* host_vtable() const { return &host_vtable_; } + /** + * The instance ID of the plugin instance this proxy belongs to. + */ + inline size_t owner_isntance_id() const { return owner_instance_id_; } static const void* CLAP_ABI host_get_extension(const struct clap_host* host, const char* extension_id); diff --git a/src/wine-host/bridges/clap.cpp b/src/wine-host/bridges/clap.cpp index 3d722d83..3971046a 100644 --- a/src/wine-host/bridges/clap.cpp +++ b/src/wine-host/bridges/clap.cpp @@ -30,8 +30,12 @@ namespace fs = ghc::filesystem; ClapPluginExtensions::ClapPluginExtensions(const clap_plugin& plugin) noexcept { } -ClapPluginInstance::ClapPluginInstance(const clap_plugin* plugin) noexcept - : plugin((assert(plugin), plugin), plugin->destroy), extensions(*plugin) {} +ClapPluginInstance::ClapPluginInstance( + const clap_plugin* plugin, + std::unique_ptr host_proxy) noexcept + : host_proxy(std::move(host_proxy)), + plugin((assert(plugin), plugin), plugin->destroy), + extensions(*plugin) {} ClapBridge::ClapBridge(MainContext& main_context, // NOLINTNEXTLINE(bugprone-easily-swappable-parameters) @@ -141,20 +145,22 @@ void ClapBridge::run() { -> clap::plugin_factory::List::Response { return main_context_ .run_in_context([&]() { - const clap_plugin_factory_t* factory = + plugin_factory_ = static_cast( (entry_->get_factory)(CLAP_PLUGIN_FACTORY_ID)); - if (!factory) { + if (!plugin_factory_) { return clap::plugin_factory::ListResponse{ .descriptors = std::nullopt}; } std::vector descriptors; const uint32_t num_plugins = - (factory->get_plugin_count)(factory); + (plugin_factory_->get_plugin_count)( + plugin_factory_); for (uint32_t i = 0; i < num_plugins; i++) { const clap_plugin_descriptor_t* descriptor = - (factory->get_plugin_descriptor)(factory, i); + (plugin_factory_->get_plugin_descriptor)( + plugin_factory_, i); if (!descriptor) { std::cerr << "Plugin returned a null pointer " "for plugin index " @@ -172,6 +178,37 @@ void ClapBridge::run() { }) .get(); }, + [&](clap::plugin_factory::Create& request) + -> clap::plugin_factory::Create::Response { + return main_context_ + .run_in_context([&]() { + // This assertion should never be hit, but you can never + // be too sure! + assert(plugin_factory_); + + // We need the instance ID before the instance exists. + // If creating the plugin fails then that's no problem + // since we're using sparse hash maps anyways. + const size_t instance_id = generate_instance_id(); + auto host_proxy = std::make_unique( + *this, instance_id, std::move(request.host)); + + const clap_plugin_t* plugin = + plugin_factory_->create_plugin( + plugin_factory_, host_proxy->host_vtable(), + request.plugin_id.c_str()); + if (plugin) { + register_plugin_instance(plugin, + std::move(host_proxy)); + return clap::plugin_factory::CreateResponse{ + .instance_id = instance_id}; + } else { + return clap::plugin_factory::CreateResponse{ + .instance_id = std::nullopt}; + } + }) + .get(); + }, }); } @@ -359,15 +396,19 @@ ClapBridge::get_instance(size_t instance_id) noexcept { // return buffer_config; // } -size_t ClapBridge::register_plugin_instance(const clap_plugin* plugin) { +void ClapBridge::register_plugin_instance( + const clap_plugin* plugin, + std::unique_ptr host_proxy) { std::unique_lock lock(object_instances_mutex_); - if (!plugin) { - throw std::invalid_argument("The plugin pointer cannot be null"); - } + assert(plugin); + assert(host_proxy); - const size_t instance_id = generate_instance_id(); - object_instances_.emplace(instance_id, plugin); + // This instance ID has already been generated because the host proxy has to + // be created before the plugin instance + const size_t instance_id = host_proxy->owner_isntance_id(); + object_instances_.emplace( + instance_id, ClapPluginInstance(plugin, std::move(host_proxy))); // Every plugin instance gets its own audio thread std::promise socket_listening_latch; @@ -413,8 +454,6 @@ size_t ClapBridge::register_plugin_instance(const clap_plugin* plugin) { // the native plugin may try to connect to it before our thread is up and // running. socket_listening_latch.get_future().wait(); - - return instance_id; } void ClapBridge::unregister_object_instance(size_t instance_id) { diff --git a/src/wine-host/bridges/clap.h b/src/wine-host/bridges/clap.h index 006a460d..f0aa4867 100644 --- a/src/wine-host/bridges/clap.h +++ b/src/wine-host/bridges/clap.h @@ -22,6 +22,7 @@ #include #include +#include #include #include "../../common/audio-shm.h" @@ -29,6 +30,7 @@ #include "../../common/configuration.h" #include "../../common/mutual-recursion.h" #include "../editor.h" +#include "clap-impls/host-proxy.h" #include "common.h" /** @@ -62,16 +64,23 @@ struct ClapPluginInstance { * Bind a CLAP plugin pointer to this plugin instance object. This can only * be done once per plugin pointer. The pointer must be non-null. */ - ClapPluginInstance(const clap_plugin* plugin) noexcept; + ClapPluginInstance(const clap_plugin* plugin, + std::unique_ptr host_proxy) noexcept; public: + /** + * A proxy for the native CLAP host. Stored using an `std::unique_ptr` + * because it must be created before creating the plugin instance, and the + * object cannot move after being created because of the vtable. + */ + std::unique_ptr host_proxy; + // TODO: Proxies for host extension objects + /** * A dedicated thread for handling incoming audio thread function calls. */ Win32Thread audio_thread_handler; - // TODO: Proxies for host extension objects - /** * A shared memory object we'll write the input audio buffers to on the * native plugin side. We'll then let the plugin write its outputs here on @@ -331,11 +340,13 @@ class ClapBridge : public HostBridge { size_t instance_id); /** - * Assign a unique identifier to an object and add it to - * `object_instances_`. This will also set up an audio thread socket - * listener for the plugin instance. + * Add a plugin and its host to it to `object_instances_`. The plugin's + * identifier is taken from the host proxy since this host proxy is already + * needed when constructing the plugin. This will also set up an audio + * thread socket listener for the plugin instance. */ - size_t register_plugin_instance(const clap_plugin* plugin); + void register_plugin_instance(const clap_plugin* plugin, + std::unique_ptr host_proxy); /** * Remove an object from `object_instances_`. Will also tear down the @@ -363,6 +374,12 @@ class ClapBridge : public HostBridge { */ std::unique_ptr entry_; + /** + * The plugin's factory, initialized when the host requests the plugin + * factory. + */ + const clap_plugin_factory_t* plugin_factory_ = nullptr; + /** * All sockets used for communicating with this specific plugin. *