From de028faf70ec40e9b9453291588925a24d9b6059 Mon Sep 17 00:00:00 2001 From: Robbert van der Helm Date: Mon, 26 Sep 2022 17:57:49 +0200 Subject: [PATCH] Fully implement the CLAP tail extension Trivial extension, but this required us to be able to send audio thread callbacks first. --- src/common/serialization/clap.h | 3 +- src/common/serialization/clap/README.md | 2 +- src/common/serialization/clap/host.cpp | 6 ++-- src/common/serialization/clap/host.h | 4 ++- src/common/serialization/clap/plugin.cpp | 6 ++-- src/common/serialization/clap/plugin.h | 2 ++ .../bridges/clap-impls/plugin-proxy.cpp | 21 ++++++++++++-- src/plugin/bridges/clap-impls/plugin-proxy.h | 11 ++++++-- src/plugin/bridges/clap.cpp | 11 ++++---- .../bridges/clap-impls/host-proxy.cpp | 28 ++++++++++++++----- src/wine-host/bridges/clap-impls/host-proxy.h | 10 +++++-- src/wine-host/bridges/clap.cpp | 15 ++++++++-- src/wine-host/bridges/clap.h | 1 + 13 files changed, 91 insertions(+), 29 deletions(-) diff --git a/src/common/serialization/clap.h b/src/common/serialization/clap.h index be4a1611..035554db 100644 --- a/src/common/serialization/clap.h +++ b/src/common/serialization/clap.h @@ -97,7 +97,8 @@ struct ClapAudioThreadControlRequest { using Payload = std::variant; + clap::ext::params::plugin::Flush, + clap::ext::tail::plugin::Get>; Payload payload; diff --git a/src/common/serialization/clap/README.md b/src/common/serialization/clap/README.md index dfb8542f..9371e6e5 100644 --- a/src/common/serialization/clap/README.md +++ b/src/common/serialization/clap/README.md @@ -26,7 +26,7 @@ Yabridge currently tracks CLAP 1.1.1. The implementation status for CLAP's core | `clap.posix-fd-support` | :x: Not supported yet | | `clap.render` | :x: Not supported yet | | `clap.state` | :x: Not supported yet | -| `clap.tail` | :x: Not supported yet | +| `clap.tail` | :heavy_check_mark: | | `clap.thread-check` | :x: Not supported yet | | `clap.thread-pool` | :x: Not supported yet | | `clap.timer-support` | :x: Not supported yet | diff --git a/src/common/serialization/clap/host.cpp b/src/common/serialization/clap/host.cpp index cfdc4e35..30b5db68 100644 --- a/src/common/serialization/clap/host.cpp +++ b/src/common/serialization/clap/host.cpp @@ -19,6 +19,7 @@ #include #include #include +#include namespace clap { namespace host { @@ -30,11 +31,12 @@ Host::Host(const clap_host_t& original) url(original.url ? std::optional(original.url) : std::nullopt), version((assert(original.version), original.version)) {} -std::array, 3> SupportedHostExtensions::list() +std::array, 4> SupportedHostExtensions::list() const noexcept { return {std::pair(supports_audio_ports, CLAP_EXT_AUDIO_PORTS), std::pair(supports_note_ports, CLAP_EXT_NOTE_PORTS), - std::pair(supports_params, CLAP_EXT_PARAMS)}; + std::pair(supports_params, CLAP_EXT_PARAMS), + std::pair(supports_tail, CLAP_EXT_TAIL)}; } } // namespace host diff --git a/src/common/serialization/clap/host.h b/src/common/serialization/clap/host.h index bd151113..6d1b52e6 100644 --- a/src/common/serialization/clap/host.h +++ b/src/common/serialization/clap/host.h @@ -84,18 +84,20 @@ struct SupportedHostExtensions { bool supports_audio_ports = false; bool supports_note_ports = false; bool supports_params = false; + bool supports_tail = false; /** * Get a list of `` tuples for the supported * extensions. Used during logging. */ - std::array, 3> list() const noexcept; + std::array, 4> list() const noexcept; template void serialize(S& s) { s.value1b(supports_audio_ports); s.value1b(supports_note_ports); s.value1b(supports_params); + s.value1b(supports_tail); } }; diff --git a/src/common/serialization/clap/plugin.cpp b/src/common/serialization/clap/plugin.cpp index 3c82e4a5..55919039 100644 --- a/src/common/serialization/clap/plugin.cpp +++ b/src/common/serialization/clap/plugin.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include "version.h" @@ -78,11 +79,12 @@ const clap_plugin_descriptor_t* Descriptor::get() const { return &clap_descriptor; } -std::array, 3> SupportedPluginExtensions::list() +std::array, 4> SupportedPluginExtensions::list() const noexcept { return {std::pair(supports_audio_ports, CLAP_EXT_AUDIO_PORTS), std::pair(supports_note_ports, CLAP_EXT_NOTE_PORTS), - std::pair(supports_params, CLAP_EXT_PARAMS)}; + std::pair(supports_params, CLAP_EXT_PARAMS), + std::pair(supports_tail, CLAP_EXT_TAIL)}; } } // namespace plugin diff --git a/src/common/serialization/clap/plugin.h b/src/common/serialization/clap/plugin.h index e4e4dc6e..addd5179 100644 --- a/src/common/serialization/clap/plugin.h +++ b/src/common/serialization/clap/plugin.h @@ -117,6 +117,7 @@ struct SupportedPluginExtensions { bool supports_audio_ports = false; bool supports_note_ports = false; bool supports_params = false; + bool supports_tail = false; /** * Get a list of `` tuples for the supported @@ -129,6 +130,7 @@ struct SupportedPluginExtensions { s.value1b(supports_audio_ports); s.value1b(supports_note_ports); s.value1b(supports_params); + s.value1b(supports_tail); } }; diff --git a/src/plugin/bridges/clap-impls/plugin-proxy.cpp b/src/plugin/bridges/clap-impls/plugin-proxy.cpp index e544b326..e1e450c0 100644 --- a/src/plugin/bridges/clap-impls/plugin-proxy.cpp +++ b/src/plugin/bridges/clap-impls/plugin-proxy.cpp @@ -24,7 +24,9 @@ ClapHostExtensions::ClapHostExtensions(const clap_host& host) noexcept note_ports(static_cast( host.get_extension(&host, CLAP_EXT_NOTE_PORTS))), params(static_cast( - host.get_extension(&host, CLAP_EXT_PARAMS))) {} + host.get_extension(&host, CLAP_EXT_PARAMS))), + tail(static_cast( + host.get_extension(&host, CLAP_EXT_TAIL))) {} ClapHostExtensions::ClapHostExtensions() noexcept {} @@ -33,7 +35,8 @@ clap::host::SupportedHostExtensions ClapHostExtensions::supported() return clap::host::SupportedHostExtensions{ .supports_audio_ports = audio_ports != nullptr, .supports_note_ports = note_ports != nullptr, - .supports_params = params != nullptr}; + .supports_params = params != nullptr, + .supports_tail = tail != nullptr}; } clap_plugin_proxy::clap_plugin_proxy(ClapPluginBridge& bridge, @@ -74,6 +77,9 @@ clap_plugin_proxy::clap_plugin_proxy(ClapPluginBridge& bridge, .text_to_value = ext_params_text_to_value, .flush = ext_params_flush, }), + ext_tail_vtable(clap_plugin_tail_t{ + .get = ext_tail_get, + }), // These function objects are relatively large, and we probably won't be // getting that many of them pending_callbacks_(128) {} @@ -210,6 +216,9 @@ clap_plugin_proxy::plugin_get_extension(const struct clap_plugin* plugin, } else if (self->supported_extensions_.supports_params && strcmp(id, CLAP_EXT_PARAMS) == 0) { extension_ptr = &self->ext_params_vtable; + } else if (self->supported_extensions_.supports_tail && + strcmp(id, CLAP_EXT_TAIL) == 0) { + extension_ptr = &self->ext_tail_vtable; } self->bridge_.logger_.log_extension_query("clap_plugin::get_extension", @@ -408,3 +417,11 @@ clap_plugin_proxy::ext_params_flush(const clap_plugin_t* plugin, self->bridge_.send_audio_thread_message( clap::ext::params::plugin::Flush{.instance_id = self->instance_id()}); } + +uint32_t CLAP_ABI clap_plugin_proxy::ext_tail_get(const clap_plugin_t* plugin) { + assert(plugin && plugin->plugin_data); + auto self = static_cast(plugin->plugin_data); + + return self->bridge_.send_audio_thread_message( + clap::ext::tail::plugin::Get{.instance_id = self->instance_id()}); +} diff --git a/src/plugin/bridges/clap-impls/plugin-proxy.h b/src/plugin/bridges/clap-impls/plugin-proxy.h index 1e634de6..b032556a 100644 --- a/src/plugin/bridges/clap-impls/plugin-proxy.h +++ b/src/plugin/bridges/clap-impls/plugin-proxy.h @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -61,6 +62,7 @@ struct ClapHostExtensions { const clap_host_audio_ports_t* audio_ports = nullptr; const clap_host_note_ports_t* note_ports = nullptr; const clap_host_params_t* params = nullptr; + const clap_host_tail_t* tail = nullptr; }; /** @@ -162,6 +164,8 @@ class clap_plugin_proxy { const clap_input_events_t* in, const clap_output_events_t* out); + static uint32_t CLAP_ABI ext_tail_get(const clap_plugin_t* plugin); + /** * Asynchronously run a function on the host's main thread, returning the * result as a future. @@ -235,9 +239,10 @@ class clap_plugin_proxy { // Extensions also have vtables. Whether or not we expose these to the host // depends on whether the plugin supported this extension when we called // `clap_plugin::init()`. - const clap_plugin_audio_ports ext_audio_ports_vtable; - const clap_plugin_note_ports ext_note_ports_vtable; - const clap_plugin_params ext_params_vtable; + const clap_plugin_audio_ports_t ext_audio_ports_vtable; + const clap_plugin_note_ports_t ext_note_ports_vtable; + const clap_plugin_params_t ext_params_vtable; + const clap_plugin_tail_t ext_tail_vtable; /** * The extensions supported by the bridged plugin. Set after a successful diff --git a/src/plugin/bridges/clap.cpp b/src/plugin/bridges/clap.cpp index 824f20c1..49a21ac3 100644 --- a/src/plugin/bridges/clap.cpp +++ b/src/plugin/bridges/clap.cpp @@ -278,12 +278,13 @@ void ClapPluginBridge::register_plugin_proxy( }, [&](const clap::ext::tail::host::Changed& request) -> clap::ext::tail::host::Changed::Response { - // FIXME: - // const auto& [instance, _] = - // get_instance(request.instance_id); + const auto& [plugin_proxy, _] = + get_proxy(request.owner_instance_id); - // return instance.plugin->start_processing( - // instance.plugin.get()); + plugin_proxy.host_extensions_.tail->changed( + plugin_proxy.host_); + + return Ack{}; }, }); }); diff --git a/src/wine-host/bridges/clap-impls/host-proxy.cpp b/src/wine-host/bridges/clap-impls/host-proxy.cpp index 3dbf63a2..e7a84607 100644 --- a/src/wine-host/bridges/clap-impls/host-proxy.cpp +++ b/src/wine-host/bridges/clap-impls/host-proxy.cpp @@ -56,6 +56,9 @@ clap_host_proxy::clap_host_proxy(ClapBridge& bridge, .rescan = ext_params_rescan, .clear = ext_params_clear, .request_flush = ext_params_request_flush, + }), + ext_tail_vtable(clap_host_tail_t{ + .changed = ext_tail_changed, }) {} const void* CLAP_ABI @@ -74,6 +77,9 @@ clap_host_proxy::host_get_extension(const struct clap_host* host, } else if (self->supported_extensions_.supports_params && strcmp(extension_id, CLAP_EXT_PARAMS) == 0) { extension_ptr = &self->ext_params_vtable; + } else if (self->supported_extensions_.supports_tail && + strcmp(extension_id, CLAP_EXT_TAIL) == 0) { + extension_ptr = &self->ext_tail_vtable; } self->bridge_.logger_.log_extension_query("clap_host::get_extension", @@ -135,7 +141,7 @@ bool CLAP_ABI clap_host_proxy::ext_audio_ports_is_rescan_flag_supported( const clap_host_t* host, uint32_t flag) { assert(host && host->host_data); - auto self = static_cast(host->host_data); + auto self = static_cast(host->host_data); return self->bridge_.send_main_thread_message( clap::ext::audio_ports::host::IsRescanFlagSupported{ @@ -145,7 +151,7 @@ bool CLAP_ABI clap_host_proxy::ext_audio_ports_is_rescan_flag_supported( void CLAP_ABI clap_host_proxy::ext_audio_ports_rescan(const clap_host_t* host, uint32_t flags) { assert(host && host->host_data); - auto self = static_cast(host->host_data); + auto self = static_cast(host->host_data); self->bridge_.send_main_thread_message(clap::ext::audio_ports::host::Rescan{ .owner_instance_id = self->owner_instance_id(), .flags = flags}); @@ -154,7 +160,7 @@ void CLAP_ABI clap_host_proxy::ext_audio_ports_rescan(const clap_host_t* host, uint32_t CLAP_ABI clap_host_proxy::ext_note_ports_supported_dialects(const clap_host_t* host) { assert(host && host->host_data); - auto self = static_cast(host->host_data); + auto self = static_cast(host->host_data); return self->bridge_.send_main_thread_message( clap::ext::note_ports::host::SupportedDialects{ @@ -164,7 +170,7 @@ clap_host_proxy::ext_note_ports_supported_dialects(const clap_host_t* host) { void CLAP_ABI clap_host_proxy::ext_note_ports_rescan(const clap_host_t* host, uint32_t flags) { assert(host && host->host_data); - auto self = static_cast(host->host_data); + auto self = static_cast(host->host_data); self->bridge_.send_main_thread_message(clap::ext::note_ports::host::Rescan{ .owner_instance_id = self->owner_instance_id(), .flags = flags}); @@ -174,7 +180,7 @@ void CLAP_ABI clap_host_proxy::ext_params_rescan(const clap_host_t* host, clap_param_rescan_flags flags) { assert(host && host->host_data); - auto self = static_cast(host->host_data); + auto self = static_cast(host->host_data); self->bridge_.send_main_thread_message(clap::ext::params::host::Rescan{ .owner_instance_id = self->owner_instance_id(), .flags = flags}); @@ -184,7 +190,7 @@ void CLAP_ABI clap_host_proxy::ext_params_clear(const clap_host_t* host, clap_id param_id, clap_param_clear_flags flags) { assert(host && host->host_data); - auto self = static_cast(host->host_data); + auto self = static_cast(host->host_data); self->bridge_.send_main_thread_message(clap::ext::params::host::Clear{ .owner_instance_id = self->owner_instance_id(), @@ -195,9 +201,17 @@ void CLAP_ABI clap_host_proxy::ext_params_clear(const clap_host_t* host, void CLAP_ABI clap_host_proxy::ext_params_request_flush(const clap_host_t* host) { assert(host && host->host_data); - auto self = static_cast(host->host_data); + auto self = static_cast(host->host_data); self->bridge_.send_audio_thread_message( clap::ext::params::host::RequestFlush{.owner_instance_id = self->owner_instance_id()}); } + +void CLAP_ABI clap_host_proxy::ext_tail_changed(const clap_host_t* host) { + assert(host && host->host_data); + auto self = static_cast(host->host_data); + + self->bridge_.send_audio_thread_message(clap::ext::tail::host::Changed{ + .owner_instance_id = self->owner_instance_id()}); +} diff --git a/src/wine-host/bridges/clap-impls/host-proxy.h b/src/wine-host/bridges/clap-impls/host-proxy.h index 833d1b2e..5a463dee 100644 --- a/src/wine-host/bridges/clap-impls/host-proxy.h +++ b/src/wine-host/bridges/clap-impls/host-proxy.h @@ -21,6 +21,7 @@ #include #include #include +#include #include #include "../../common/serialization/clap/plugin-factory.h" @@ -83,6 +84,8 @@ class clap_host_proxy { clap_param_clear_flags flags); static void CLAP_ABI ext_params_request_flush(const clap_host_t* host); + static void CLAP_ABI ext_tail_changed(const clap_host_t* host); + /** * The extensions supported by the host, set just before calling * `clap_plugin::init()` on the bridged plugin. We'll allow the plugin to @@ -105,9 +108,10 @@ class clap_host_proxy { // Extensions also have vtables. Whether or not we expose these to the host // depends on whether the plugin supported this extension when the host // called `clap_plugin::init()`. - const clap_host_audio_ports ext_audio_ports_vtable; - const clap_host_note_ports ext_note_ports_vtable; - const clap_host_params ext_params_vtable; + const clap_host_audio_ports_t ext_audio_ports_vtable; + const clap_host_note_ports_t ext_note_ports_vtable; + const clap_host_params_t ext_params_vtable; + const clap_host_tail_t ext_tail_vtable; /** * Keeps track of whether there are pending host callbacks. Used to prevent diff --git a/src/wine-host/bridges/clap.cpp b/src/wine-host/bridges/clap.cpp index 8bf6b631..d78b9d2d 100644 --- a/src/wine-host/bridges/clap.cpp +++ b/src/wine-host/bridges/clap.cpp @@ -32,7 +32,9 @@ ClapPluginExtensions::ClapPluginExtensions(const clap_plugin& plugin) noexcept note_ports(static_cast( plugin.get_extension(&plugin, CLAP_EXT_NOTE_PORTS))), params(static_cast( - plugin.get_extension(&plugin, CLAP_EXT_PARAMS))) {} + plugin.get_extension(&plugin, CLAP_EXT_PARAMS))), + tail(static_cast( + plugin.get_extension(&plugin, CLAP_EXT_TAIL))) {} ClapPluginExtensions::ClapPluginExtensions() noexcept {} @@ -41,7 +43,8 @@ clap::plugin::SupportedPluginExtensions ClapPluginExtensions::supported() return clap::plugin::SupportedPluginExtensions{ .supports_audio_ports = audio_ports != nullptr, .supports_note_ports = note_ports != nullptr, - .supports_params = params != nullptr}; + .supports_params = params != nullptr, + .supports_tail = tail != nullptr}; } ClapPluginInstance::ClapPluginInstance( @@ -660,6 +663,14 @@ void ClapBridge::register_plugin_instance( return clap::ext::params::plugin::FlushResponse{}; }, + [&](const clap::ext::tail::plugin::Get& request) + -> clap::ext::tail::plugin::Get::Response { + const auto& [instance, _] = + get_instance(request.instance_id); + + return instance.extensions.tail->get( + instance.plugin.get()); + }, }); }); diff --git a/src/wine-host/bridges/clap.h b/src/wine-host/bridges/clap.h index 66bfaf23..d07d4e66 100644 --- a/src/wine-host/bridges/clap.h +++ b/src/wine-host/bridges/clap.h @@ -69,6 +69,7 @@ struct ClapPluginExtensions { const clap_plugin_audio_ports_t* audio_ports = nullptr; const clap_plugin_note_ports_t* note_ports = nullptr; const clap_plugin_params_t* params = nullptr; + const clap_plugin_tail_t* tail = nullptr; }; /**