From abaaaeed3c0b853ff10c0cd9cc0d003f7010109d Mon Sep 17 00:00:00 2001 From: Robbert van der Helm Date: Fri, 21 Oct 2022 16:44:02 +0200 Subject: [PATCH] Implement CLAP audio-ports-config extension --- src/common/serialization/clap.h | 4 ++ src/common/serialization/clap/README.md | 2 +- src/common/serialization/clap/host.cpp | 4 +- src/common/serialization/clap/host.h | 4 +- src/common/serialization/clap/plugin.cpp | 4 +- src/common/serialization/clap/plugin.h | 4 +- .../bridges/clap-impls/plugin-proxy.cpp | 52 +++++++++++++++++++ src/plugin/bridges/clap-impls/plugin-proxy.h | 13 +++++ src/plugin/bridges/clap.cpp | 16 ++++++ .../bridges/clap-impls/host-proxy.cpp | 16 ++++++ src/wine-host/bridges/clap-impls/host-proxy.h | 4 ++ src/wine-host/bridges/clap.cpp | 42 +++++++++++++++ src/wine-host/bridges/clap.h | 1 + 13 files changed, 161 insertions(+), 5 deletions(-) diff --git a/src/common/serialization/clap.h b/src/common/serialization/clap.h index 5b6bdcaa..2b123207 100644 --- a/src/common/serialization/clap.h +++ b/src/common/serialization/clap.h @@ -62,6 +62,9 @@ using ClapMainThreadControlRequest = clap::plugin::Deactivate, clap::ext::audio_ports::plugin::Count, clap::ext::audio_ports::plugin::Get, + clap::ext::audio_ports_config::plugin::Count, + clap::ext::audio_ports_config::plugin::Get, + clap::ext::audio_ports_config::plugin::Select, clap::ext::gui::plugin::IsApiSupported, clap::ext::gui::plugin::Create, clap::ext::gui::plugin::Destroy, @@ -173,6 +176,7 @@ using ClapMainThreadCallbackRequest = clap::ext::latency::host::Changed, clap::ext::audio_ports::host::IsRescanFlagSupported, clap::ext::audio_ports::host::Rescan, + clap::ext::audio_ports_config::host::Rescan, clap::ext::gui::host::ResizeHintsChanged, clap::ext::gui::host::RequestResize, clap::ext::gui::host::RequestShow, diff --git a/src/common/serialization/clap/README.md b/src/common/serialization/clap/README.md index bf34f0b5..301dd422 100644 --- a/src/common/serialization/clap/README.md +++ b/src/common/serialization/clap/README.md @@ -18,7 +18,7 @@ Yabridge currently tracks CLAP 1.1.1. The implementation status for CLAP's core | extension | status | | ------------------------- | ------------------------------------------------------------ | | `clap.audio-ports` | :heavy_check_mark: | -| `clap.audio-ports-config` | :x: Not supported yet | +| `clap.audio-ports-config` | :heavy_check_mark: | | `clap.event-registry` | :x: Not needed for any of the supported extensions | | `clap.gui` | :heavy_check_mark: Currently only does embedded GUIs | | `clap.latency` | :heavy_check_mark: | diff --git a/src/common/serialization/clap/host.cpp b/src/common/serialization/clap/host.cpp index 27969a4b..6ed64b39 100644 --- a/src/common/serialization/clap/host.cpp +++ b/src/common/serialization/clap/host.cpp @@ -16,6 +16,7 @@ #include "host.h" +#include #include #include #include @@ -36,9 +37,10 @@ Host::Host(const clap_host_t& original) url(original.url ? std::optional(original.url) : std::nullopt), version((assert(original.version), original.version)) {} -std::array, 9> SupportedHostExtensions::list() +std::array, 10> SupportedHostExtensions::list() const noexcept { return {std::pair(supports_audio_ports, CLAP_EXT_AUDIO_PORTS), + std::pair(supports_audio_ports_config, CLAP_EXT_AUDIO_PORTS_CONFIG), std::pair(supports_gui, CLAP_EXT_GUI), std::pair(supports_latency, CLAP_EXT_LATENCY), std::pair(supports_log, CLAP_EXT_LOG), diff --git a/src/common/serialization/clap/host.h b/src/common/serialization/clap/host.h index 3db658c8..8b42419d 100644 --- a/src/common/serialization/clap/host.h +++ b/src/common/serialization/clap/host.h @@ -82,6 +82,7 @@ struct Host { struct SupportedHostExtensions { // Don't forget to add new extensions to below method bool supports_audio_ports = false; + bool supports_audio_ports_config = false; bool supports_gui = false; bool supports_latency = false; bool supports_log = false; @@ -95,11 +96,12 @@ struct SupportedHostExtensions { * Get a list of `` tuples for the supported * extensions. Used during logging. */ - std::array, 9> list() const noexcept; + std::array, 10> list() const noexcept; template void serialize(S& s) { s.value1b(supports_audio_ports); + s.value1b(supports_audio_ports_config); s.value1b(supports_gui); s.value1b(supports_latency); s.value1b(supports_log); diff --git a/src/common/serialization/clap/plugin.cpp b/src/common/serialization/clap/plugin.cpp index 79be7775..e565b720 100644 --- a/src/common/serialization/clap/plugin.cpp +++ b/src/common/serialization/clap/plugin.cpp @@ -16,6 +16,7 @@ #include "plugin.h" +#include #include #include #include @@ -84,9 +85,10 @@ const clap_plugin_descriptor_t* Descriptor::get() const { return &clap_descriptor; } -std::array, 9> SupportedPluginExtensions::list() +std::array, 10> SupportedPluginExtensions::list() const noexcept { return {std::pair(supports_audio_ports, CLAP_EXT_AUDIO_PORTS), + std::pair(supports_audio_ports_config, CLAP_EXT_AUDIO_PORTS_CONFIG), std::pair(supports_gui, CLAP_EXT_GUI), std::pair(supports_latency, CLAP_EXT_LATENCY), std::pair(supports_note_ports, CLAP_EXT_NOTE_PORTS), diff --git a/src/common/serialization/clap/plugin.h b/src/common/serialization/clap/plugin.h index b9663bab..6392dcc3 100644 --- a/src/common/serialization/clap/plugin.h +++ b/src/common/serialization/clap/plugin.h @@ -116,6 +116,7 @@ struct Descriptor { struct SupportedPluginExtensions { // Don't forget to add new extensions to below method bool supports_audio_ports = false; + bool supports_audio_ports_config = false; bool supports_gui = false; bool supports_latency = false; bool supports_note_ports = false; @@ -129,11 +130,12 @@ struct SupportedPluginExtensions { * Get a list of `` tuples for the supported * extensions. Used during logging. */ - std::array, 9> list() const noexcept; + std::array, 10> list() const noexcept; template void serialize(S& s) { s.value1b(supports_audio_ports); + s.value1b(supports_audio_ports_config); s.value1b(supports_gui); s.value1b(supports_latency); s.value1b(supports_note_ports); diff --git a/src/plugin/bridges/clap-impls/plugin-proxy.cpp b/src/plugin/bridges/clap-impls/plugin-proxy.cpp index 9d862e29..47d972c1 100644 --- a/src/plugin/bridges/clap-impls/plugin-proxy.cpp +++ b/src/plugin/bridges/clap-impls/plugin-proxy.cpp @@ -21,6 +21,8 @@ ClapHostExtensions::ClapHostExtensions(const clap_host& host) noexcept : audio_ports(static_cast( host.get_extension(&host, CLAP_EXT_AUDIO_PORTS))), + audio_ports_config(static_cast( + host.get_extension(&host, CLAP_EXT_AUDIO_PORTS_CONFIG))), gui(static_cast( host.get_extension(&host, CLAP_EXT_GUI))), latency(static_cast( @@ -44,6 +46,7 @@ clap::host::SupportedHostExtensions ClapHostExtensions::supported() const noexcept { return clap::host::SupportedHostExtensions{ .supports_audio_ports = audio_ports != nullptr, + .supports_audio_ports_config = audio_ports_config != nullptr, .supports_gui = gui != nullptr, .supports_latency = latency != nullptr, .supports_log = log != nullptr, @@ -80,6 +83,11 @@ clap_plugin_proxy::clap_plugin_proxy(ClapPluginBridge& bridge, .count = ext_audio_ports_count, .get = ext_audio_ports_get, }), + ext_audio_ports_config_vtable(clap_plugin_audio_ports_config_t{ + .count = ext_audio_ports_config_count, + .get = ext_audio_ports_config_get, + .select = ext_audio_ports_config_select, + }), ext_gui_vtable(clap_plugin_gui_t{ .is_api_supported = ext_gui_is_api_supported, .get_preferred_api = ext_gui_get_preferred_api, @@ -302,6 +310,9 @@ clap_plugin_proxy::plugin_get_extension(const struct clap_plugin* plugin, if (self->supported_extensions_.supports_audio_ports && strcmp(id, CLAP_EXT_AUDIO_PORTS) == 0) { extension_ptr = &self->ext_audio_ports_vtable; + } else if (self->supported_extensions_.supports_audio_ports_config && + strcmp(id, CLAP_EXT_AUDIO_PORTS_CONFIG) == 0) { + extension_ptr = &self->ext_audio_ports_config_vtable; } else if (self->supported_extensions_.supports_gui && strcmp(id, CLAP_EXT_GUI) == 0) { extension_ptr = &self->ext_gui_vtable; @@ -381,6 +392,47 @@ clap_plugin_proxy::ext_audio_ports_get(const clap_plugin_t* plugin, } } +uint32_t CLAP_ABI +clap_plugin_proxy::ext_audio_ports_config_count(const clap_plugin_t* plugin) { + assert(plugin && plugin->plugin_data); + auto self = static_cast(plugin->plugin_data); + + return self->bridge_.send_main_thread_message( + clap::ext::audio_ports_config::plugin::Count{.instance_id = + self->instance_id()}); +} + +bool CLAP_ABI clap_plugin_proxy::ext_audio_ports_config_get( + const clap_plugin_t* plugin, + uint32_t index, + clap_audio_ports_config_t* config) { + assert(plugin && plugin->plugin_data && config); + auto self = static_cast(plugin->plugin_data); + + const clap::ext::audio_ports_config::plugin::GetResponse response = + self->bridge_.send_main_thread_message( + clap::ext::audio_ports_config::plugin::Get{ + .instance_id = self->instance_id(), .index = index}); + if (response.result) { + response.result->reconstruct(*config); + + return true; + } else { + return false; + } +} + +bool CLAP_ABI +clap_plugin_proxy::ext_audio_ports_config_select(const clap_plugin_t* plugin, + clap_id config_id) { + assert(plugin && plugin->plugin_data); + auto self = static_cast(plugin->plugin_data); + + return self->bridge_.send_main_thread_message( + clap::ext::audio_ports_config::plugin::Select{ + .instance_id = self->instance_id(), .config_id = config_id}); +} + bool CLAP_ABI clap_plugin_proxy::ext_gui_is_api_supported(const clap_plugin_t* plugin, const char* api, diff --git a/src/plugin/bridges/clap-impls/plugin-proxy.h b/src/plugin/bridges/clap-impls/plugin-proxy.h index 066ba273..ac696d22 100644 --- a/src/plugin/bridges/clap-impls/plugin-proxy.h +++ b/src/plugin/bridges/clap-impls/plugin-proxy.h @@ -20,6 +20,7 @@ #include #include +#include #include #include #include @@ -66,6 +67,7 @@ struct ClapHostExtensions { clap::host::SupportedHostExtensions supported() const noexcept; const clap_host_audio_ports_t* audio_ports = nullptr; + const clap_host_audio_ports_config_t* audio_ports_config = nullptr; const clap_host_gui_t* gui = nullptr; const clap_host_latency_t* latency = nullptr; const clap_host_log_t* log = nullptr; @@ -196,6 +198,16 @@ class clap_plugin_proxy { bool is_input, clap_audio_port_info_t* info); + static uint32_t CLAP_ABI + ext_audio_ports_config_count(const clap_plugin_t* plugin); + static bool CLAP_ABI + ext_audio_ports_config_get(const clap_plugin_t* plugin, + uint32_t index, + clap_audio_ports_config_t* config); + static bool CLAP_ABI + ext_audio_ports_config_select(const clap_plugin_t* plugin, + clap_id config_id); + static bool CLAP_ABI ext_gui_is_api_supported(const clap_plugin_t* plugin, const char* api, bool is_floating); @@ -331,6 +343,7 @@ class clap_plugin_proxy { // depends on whether the plugin supported this extension when we called // `clap_plugin::init()`. const clap_plugin_audio_ports_t ext_audio_ports_vtable; + const clap_plugin_audio_ports_config_t ext_audio_ports_config_vtable; const clap_plugin_gui_t ext_gui_vtable; const clap_plugin_latency_t ext_latency_vtable; const clap_plugin_note_ports_t ext_note_ports_vtable; diff --git a/src/plugin/bridges/clap.cpp b/src/plugin/bridges/clap.cpp index ce78d86f..7bf0bfe1 100644 --- a/src/plugin/bridges/clap.cpp +++ b/src/plugin/bridges/clap.cpp @@ -115,6 +115,22 @@ ClapPluginBridge::ClapPluginBridge(const ghc::filesystem::path& plugin_path) return Ack{}; }, + [&](const clap::ext::audio_ports_config::host::Rescan& request) + -> clap::ext::audio_ports_config::host::Rescan::Response { + const auto& [plugin_proxy, _] = + get_proxy(request.owner_instance_id); + + plugin_proxy + .run_on_main_thread( + [&, host = plugin_proxy.host_, + audio_ports_config = plugin_proxy.host_extensions_ + .audio_ports_config]() { + audio_ports_config->rescan(host); + }) + .wait(); + + return Ack{}; + }, [&](const clap::ext::gui::host::ResizeHintsChanged& request) -> clap::ext::gui::host::ResizeHintsChanged::Response { const auto& [plugin_proxy, _] = diff --git a/src/wine-host/bridges/clap-impls/host-proxy.cpp b/src/wine-host/bridges/clap-impls/host-proxy.cpp index eeee0774..56cf85c0 100644 --- a/src/wine-host/bridges/clap-impls/host-proxy.cpp +++ b/src/wine-host/bridges/clap-impls/host-proxy.cpp @@ -53,6 +53,9 @@ clap_host_proxy::clap_host_proxy(ClapBridge& bridge, .is_rescan_flag_supported = ext_audio_ports_is_rescan_flag_supported, .rescan = ext_audio_ports_rescan, }), + ext_audio_ports_config_vtable(clap_host_audio_ports_config_t{ + .rescan = ext_audio_ports_config_rescan, + }), ext_gui_vtable(clap_host_gui_t{ .resize_hints_changed = ext_gui_resize_hints_changed, .request_resize = ext_gui_request_resize, @@ -99,6 +102,9 @@ clap_host_proxy::host_get_extension(const struct clap_host* host, if (self->supported_extensions_.supports_audio_ports && strcmp(extension_id, CLAP_EXT_AUDIO_PORTS) == 0) { extension_ptr = &self->ext_audio_ports_vtable; + } else if (self->supported_extensions_.supports_audio_ports_config && + strcmp(extension_id, CLAP_EXT_AUDIO_PORTS_CONFIG) == 0) { + extension_ptr = &self->ext_audio_ports_config_vtable; } else if (self->supported_extensions_.supports_gui && strcmp(extension_id, CLAP_EXT_GUI) == 0) { extension_ptr = &self->ext_gui_vtable; @@ -208,6 +214,16 @@ void CLAP_ABI clap_host_proxy::ext_audio_ports_rescan(const clap_host_t* host, .owner_instance_id = self->owner_instance_id(), .flags = flags}); } +void CLAP_ABI +clap_host_proxy::ext_audio_ports_config_rescan(const clap_host_t* host) { + assert(host && host->host_data); + auto self = static_cast(host->host_data); + + self->bridge_.send_mutually_recursive_main_thread_message( + clap::ext::audio_ports_config::host::Rescan{ + .owner_instance_id = self->owner_instance_id()}); +} + void CLAP_ABI clap_host_proxy::ext_gui_resize_hints_changed(const clap_host_t* host) { assert(host && host->host_data); diff --git a/src/wine-host/bridges/clap-impls/host-proxy.h b/src/wine-host/bridges/clap-impls/host-proxy.h index 31ad9df4..78937ff0 100644 --- a/src/wine-host/bridges/clap-impls/host-proxy.h +++ b/src/wine-host/bridges/clap-impls/host-proxy.h @@ -18,6 +18,7 @@ #include +#include #include #include #include @@ -86,6 +87,8 @@ class clap_host_proxy { static void CLAP_ABI ext_audio_ports_rescan(const clap_host_t* host, uint32_t flags); + static void CLAP_ABI ext_audio_ports_config_rescan(const clap_host_t* host); + static void CLAP_ABI ext_gui_resize_hints_changed(const clap_host_t* host); static bool CLAP_ABI ext_gui_request_resize(const clap_host_t* host, uint32_t width, @@ -140,6 +143,7 @@ class clap_host_proxy { // depends on whether the plugin supported this extension when the host // called `clap_plugin::init()`. const clap_host_audio_ports_t ext_audio_ports_vtable; + const clap_host_audio_ports_config_t ext_audio_ports_config_vtable; const clap_host_gui_t ext_gui_vtable; const clap_host_latency_t ext_latency_vtable; // This is also always available regardless of the proxied host. That way we diff --git a/src/wine-host/bridges/clap.cpp b/src/wine-host/bridges/clap.cpp index cd850b2c..7bd421da 100644 --- a/src/wine-host/bridges/clap.cpp +++ b/src/wine-host/bridges/clap.cpp @@ -29,6 +29,8 @@ namespace fs = ghc::filesystem; ClapPluginExtensions::ClapPluginExtensions(const clap_plugin& plugin) noexcept : audio_ports(static_cast( plugin.get_extension(&plugin, CLAP_EXT_AUDIO_PORTS))), + audio_ports_config(static_cast( + plugin.get_extension(&plugin, CLAP_EXT_AUDIO_PORTS_CONFIG))), gui(static_cast( plugin.get_extension(&plugin, CLAP_EXT_GUI))), latency(static_cast( @@ -52,6 +54,7 @@ clap::plugin::SupportedPluginExtensions ClapPluginExtensions::supported() const noexcept { return clap::plugin::SupportedPluginExtensions{ .supports_audio_ports = audio_ports != nullptr, + .supports_audio_ports_config = audio_ports_config != nullptr, .supports_gui = gui != nullptr, .supports_latency = latency != nullptr, .supports_note_ports = note_ports != nullptr, @@ -357,6 +360,45 @@ void ClapBridge::run() { .result = std::nullopt}; } }, + [&](const clap::ext::audio_ports_config::plugin::Count& request) + -> clap::ext::audio_ports_config::plugin::Count::Response { + const auto& [instance, _] = get_instance(request.instance_id); + + // We'll ignore the main thread requirement for simple array + // lookups to avoid the synchronisation costs in hot code paths + return instance.extensions.audio_ports_config->count( + instance.plugin.get()); + }, + [&](const clap::ext::audio_ports_config::plugin::Get& request) + -> clap::ext::audio_ports_config::plugin::Get::Response { + const auto& [instance, _] = get_instance(request.instance_id); + + // We'll ignore the main thread requirement for simple array + // lookups to avoid the synchronisation costs in hot code paths + clap_audio_ports_config_t config{}; + if (instance.extensions.audio_ports_config->get( + instance.plugin.get(), request.index, &config)) { + return clap::ext::audio_ports_config::plugin::GetResponse{ + .result = config}; + } else { + return clap::ext::audio_ports_config::plugin::GetResponse{ + .result = std::nullopt}; + } + }, + [&](const clap::ext::audio_ports_config::plugin::Select& request) + -> clap::ext::audio_ports_config::plugin::Select::Response { + const auto& [instance, _] = get_instance(request.instance_id); + + return main_context_ + .run_in_context( + [&, plugin = instance.plugin.get(), + audio_ports_config = + instance.extensions.audio_ports_config]() { + return audio_ports_config->select( + plugin, request.config_id); + }) + .get(); + }, [&](const clap::ext::gui::plugin::IsApiSupported& request) -> clap::ext::gui::plugin::IsApiSupported::Response { const auto& [instance, _] = get_instance(request.instance_id); diff --git a/src/wine-host/bridges/clap.h b/src/wine-host/bridges/clap.h index 4b7c70ac..cbc208c0 100644 --- a/src/wine-host/bridges/clap.h +++ b/src/wine-host/bridges/clap.h @@ -67,6 +67,7 @@ struct ClapPluginExtensions { clap::plugin::SupportedPluginExtensions supported() const noexcept; const clap_plugin_audio_ports_t* audio_ports = nullptr; + const clap_plugin_audio_ports_config_t* audio_ports_config = nullptr; const clap_plugin_gui_t* gui = nullptr; const clap_plugin_latency_t* latency = nullptr; const clap_plugin_note_ports_t* note_ports = nullptr;