Implement Wine side for creating plugin instances

This commit is contained in:
Robbert van der Helm
2022-09-08 00:11:04 +02:00
parent e74c25445d
commit 423534f373
6 changed files with 113 additions and 26 deletions
+24 -2
View File
@@ -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 = <clap_host_t*>, "
"plugin_id = \""
<< request.plugin_id << "\")";
});
}
bool ClapLogger::log_request(bool is_host_plugin, const WantsConfiguration&) { bool ClapLogger::log_request(bool is_host_plugin, const WantsConfiguration&) {
return log_request_base(is_host_plugin, [&](auto& message) { return log_request_base(is_host_plugin, [&](auto& message) {
message << "Requesting <Configuration>"; message << "Requesting <Configuration>";
@@ -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, bool is_host_plugin,
const clap::plugin_factory::ListResponse& response) { 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) { if (response.descriptors) {
message << "<clap_plugin_factory containing " message << "<clap_plugin_factory containing "
<< response.descriptors->size() << " plugin descriptors>"; << response.descriptors->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 << "<clap_plugin_t* with instance ID "
<< *response.instance_id << ">";
} else {
message << "<nullptr*>";
}
});
}
void ClapLogger::log_response(bool is_host_plugin, const Configuration&) { void ClapLogger::log_response(bool is_host_plugin, const Configuration&) {
log_response_base(is_host_plugin, log_response_base(is_host_plugin,
[&](auto& message) { message << "<Configuration>"; }); [&](auto& message) { message << "<Configuration>"; });
+4 -1
View File
@@ -46,6 +46,7 @@ class ClapLogger {
// log message for the response together with the request. // 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::List&);
bool log_request(bool is_host_plugin, const clap::plugin_factory::Create&);
// TODO: Audio thread requests // TODO: Audio thread requests
// bool log_request(bool is_host_plugin, // bool log_request(bool is_host_plugin,
@@ -54,8 +55,10 @@ class ClapLogger {
bool log_request(bool is_host_plugin, const WantsConfiguration&); bool log_request(bool is_host_plugin, const WantsConfiguration&);
// void log_response(bool is_host_plugin, const Ack&); // 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&); const clap::plugin_factory::ListResponse&);
void log_response(bool is_host_plugin,
const clap::plugin_factory::CreateResponse&);
// TODO: Audio thread responses // TODO: Audio thread responses
// void log_response(bool is_host_plugin, // void log_response(bool is_host_plugin,
+4 -2
View File
@@ -22,6 +22,7 @@
#include "../bitsery/ext/message-reference.h" #include "../bitsery/ext/message-reference.h"
#include "../utils.h" #include "../utils.h"
#include "clap/host.h"
#include "clap/plugin-factory.h" #include "clap/plugin-factory.h"
#include "common.h" #include "common.h"
@@ -38,8 +39,9 @@
*/ */
// FIXME: Remove the `WantsConfiguration`. For some reason bitsery just won't // FIXME: Remove the `WantsConfiguration`. For some reason bitsery just won't
// serialize this without it. // serialize this without it.
using ClapMainThreadControlRequest = using ClapMainThreadControlRequest = std::variant<WantsConfiguration,
std::variant<WantsConfiguration, clap::plugin_factory::List>; clap::plugin_factory::List,
clap::plugin_factory::Create>;
template <typename S> template <typename S>
void serialize(S& s, ClapMainThreadControlRequest& payload) { void serialize(S& s, ClapMainThreadControlRequest& payload) {
@@ -49,6 +49,10 @@ class clap_host_proxy {
* Get a `clap_host` vtable that can be passed to the plugin. * Get a `clap_host` vtable that can be passed to the plugin.
*/ */
inline const clap_host_t* host_vtable() const { return &host_vtable_; } 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, static const void* CLAP_ABI host_get_extension(const struct clap_host* host,
const char* extension_id); const char* extension_id);
+53 -14
View File
@@ -30,8 +30,12 @@ namespace fs = ghc::filesystem;
ClapPluginExtensions::ClapPluginExtensions(const clap_plugin& plugin) noexcept { ClapPluginExtensions::ClapPluginExtensions(const clap_plugin& plugin) noexcept {
} }
ClapPluginInstance::ClapPluginInstance(const clap_plugin* plugin) noexcept ClapPluginInstance::ClapPluginInstance(
: plugin((assert(plugin), plugin), plugin->destroy), extensions(*plugin) {} const clap_plugin* plugin,
std::unique_ptr<clap_host_proxy> host_proxy) noexcept
: host_proxy(std::move(host_proxy)),
plugin((assert(plugin), plugin), plugin->destroy),
extensions(*plugin) {}
ClapBridge::ClapBridge(MainContext& main_context, ClapBridge::ClapBridge(MainContext& main_context,
// NOLINTNEXTLINE(bugprone-easily-swappable-parameters) // NOLINTNEXTLINE(bugprone-easily-swappable-parameters)
@@ -141,20 +145,22 @@ void ClapBridge::run() {
-> clap::plugin_factory::List::Response { -> clap::plugin_factory::List::Response {
return main_context_ return main_context_
.run_in_context([&]() { .run_in_context([&]() {
const clap_plugin_factory_t* factory = plugin_factory_ =
static_cast<const clap_plugin_factory_t*>( static_cast<const clap_plugin_factory_t*>(
(entry_->get_factory)(CLAP_PLUGIN_FACTORY_ID)); (entry_->get_factory)(CLAP_PLUGIN_FACTORY_ID));
if (!factory) { if (!plugin_factory_) {
return clap::plugin_factory::ListResponse{ return clap::plugin_factory::ListResponse{
.descriptors = std::nullopt}; .descriptors = std::nullopt};
} }
std::vector<clap::plugin::Descriptor> descriptors; std::vector<clap::plugin::Descriptor> descriptors;
const uint32_t num_plugins = 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++) { for (uint32_t i = 0; i < num_plugins; i++) {
const clap_plugin_descriptor_t* descriptor = const clap_plugin_descriptor_t* descriptor =
(factory->get_plugin_descriptor)(factory, i); (plugin_factory_->get_plugin_descriptor)(
plugin_factory_, i);
if (!descriptor) { if (!descriptor) {
std::cerr << "Plugin returned a null pointer " std::cerr << "Plugin returned a null pointer "
"for plugin index " "for plugin index "
@@ -172,6 +178,37 @@ void ClapBridge::run() {
}) })
.get(); .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<clap_host_proxy>(
*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; // 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<clap_host_proxy> host_proxy) {
std::unique_lock lock(object_instances_mutex_); std::unique_lock lock(object_instances_mutex_);
if (!plugin) { assert(plugin);
throw std::invalid_argument("The plugin pointer cannot be null"); assert(host_proxy);
}
const size_t instance_id = generate_instance_id(); // This instance ID has already been generated because the host proxy has to
object_instances_.emplace(instance_id, plugin); // 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 // Every plugin instance gets its own audio thread
std::promise<void> socket_listening_latch; std::promise<void> 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 // the native plugin may try to connect to it before our thread is up and
// running. // running.
socket_listening_latch.get_future().wait(); socket_listening_latch.get_future().wait();
return instance_id;
} }
void ClapBridge::unregister_object_instance(size_t instance_id) { void ClapBridge::unregister_object_instance(size_t instance_id) {
+24 -7
View File
@@ -22,6 +22,7 @@
#include <string> #include <string>
#include <clap/entry.h> #include <clap/entry.h>
#include <clap/plugin-factory.h>
#include <clap/plugin.h> #include <clap/plugin.h>
#include "../../common/audio-shm.h" #include "../../common/audio-shm.h"
@@ -29,6 +30,7 @@
#include "../../common/configuration.h" #include "../../common/configuration.h"
#include "../../common/mutual-recursion.h" #include "../../common/mutual-recursion.h"
#include "../editor.h" #include "../editor.h"
#include "clap-impls/host-proxy.h"
#include "common.h" #include "common.h"
/** /**
@@ -62,16 +64,23 @@ struct ClapPluginInstance {
* Bind a CLAP plugin pointer to this plugin instance object. This can only * 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. * 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<clap_host_proxy> host_proxy) noexcept;
public: 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<clap_host_proxy> host_proxy;
// TODO: Proxies for host extension objects
/** /**
* A dedicated thread for handling incoming audio thread function calls. * A dedicated thread for handling incoming audio thread function calls.
*/ */
Win32Thread audio_thread_handler; Win32Thread audio_thread_handler;
// TODO: Proxies for host extension objects
/** /**
* A shared memory object we'll write the input audio buffers to on the * 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 * 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); size_t instance_id);
/** /**
* Assign a unique identifier to an object and add it to * Add a plugin and its host to it to `object_instances_`. The plugin's
* `object_instances_`. This will also set up an audio thread socket * identifier is taken from the host proxy since this host proxy is already
* listener for the plugin instance. * 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<clap_host_proxy> host_proxy);
/** /**
* Remove an object from `object_instances_`. Will also tear down the * Remove an object from `object_instances_`. Will also tear down the
@@ -363,6 +374,12 @@ class ClapBridge : public HostBridge {
*/ */
std::unique_ptr<clap_plugin_entry, void (*)(clap_plugin_entry*)> entry_; std::unique_ptr<clap_plugin_entry, void (*)(clap_plugin_entry*)> 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. * All sockets used for communicating with this specific plugin.
* *