From a4d5748c05138a0b943699e89b5bfac50a77b166 Mon Sep 17 00:00:00 2001 From: Robbert van der Helm Date: Sun, 9 Oct 2022 23:19:53 +0200 Subject: [PATCH] Implement the voice-info CLAP extension --- src/common/serialization/clap.h | 6 ++-- 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 | 4 ++- .../bridges/clap-impls/plugin-proxy.cpp | 31 +++++++++++++++++-- src/plugin/bridges/clap-impls/plugin-proxy.h | 6 ++++ src/plugin/bridges/clap.cpp | 16 ++++++++++ .../bridges/clap-impls/host-proxy.cpp | 14 +++++++++ src/wine-host/bridges/clap-impls/host-proxy.h | 4 +++ src/wine-host/bridges/clap.cpp | 26 ++++++++++++++-- src/wine-host/bridges/clap.h | 1 + 13 files changed, 113 insertions(+), 13 deletions(-) diff --git a/src/common/serialization/clap.h b/src/common/serialization/clap.h index 25223e7b..290afd65 100644 --- a/src/common/serialization/clap.h +++ b/src/common/serialization/clap.h @@ -81,7 +81,8 @@ using ClapMainThreadControlRequest = clap::ext::params::plugin::ValueToText, clap::ext::params::plugin::TextToValue, clap::ext::state::plugin::Save, - clap::ext::state::plugin::Load>; + clap::ext::state::plugin::Load, + clap::ext::voice_info::plugin::Get>; template void serialize(S& s, ClapMainThreadControlRequest& payload) { @@ -177,7 +178,8 @@ using ClapMainThreadCallbackRequest = clap::ext::note_ports::host::Rescan, clap::ext::params::host::Rescan, clap::ext::params::host::Clear, - clap::ext::state::host::MarkDirty>; + clap::ext::state::host::MarkDirty, + clap::ext::voice_info::host::Changed>; template void serialize(S& s, ClapMainThreadCallbackRequest& payload) { diff --git a/src/common/serialization/clap/README.md b/src/common/serialization/clap/README.md index 8d7d048d..ec015030 100644 --- a/src/common/serialization/clap/README.md +++ b/src/common/serialization/clap/README.md @@ -33,7 +33,7 @@ Yabridge currently tracks CLAP 1.1.1. The implementation status for CLAP's core | `clap.thread-check` | :heavy_check_mark: No bridging involved | | `clap.thread-pool` | :x: Not supported yet | | `clap.timer-support` | :x: Not supported yet | -| `clap.voice-info` | :x: Not supported yet | +| `clap.voice-info` | :heavy_check_mark: | | draft extension | status | | -------------------------------- | --------------------- | diff --git a/src/common/serialization/clap/host.cpp b/src/common/serialization/clap/host.cpp index 096a769f..27969a4b 100644 --- a/src/common/serialization/clap/host.cpp +++ b/src/common/serialization/clap/host.cpp @@ -24,6 +24,7 @@ #include #include #include +#include namespace clap { namespace host { @@ -35,7 +36,7 @@ Host::Host(const clap_host_t& original) url(original.url ? std::optional(original.url) : std::nullopt), version((assert(original.version), original.version)) {} -std::array, 8> SupportedHostExtensions::list() +std::array, 9> SupportedHostExtensions::list() const noexcept { return {std::pair(supports_audio_ports, CLAP_EXT_AUDIO_PORTS), std::pair(supports_gui, CLAP_EXT_GUI), @@ -44,7 +45,8 @@ std::array, 8> SupportedHostExtensions::list() std::pair(supports_note_ports, CLAP_EXT_NOTE_PORTS), std::pair(supports_params, CLAP_EXT_PARAMS), std::pair(supports_state, CLAP_EXT_STATE), - std::pair(supports_tail, CLAP_EXT_TAIL)}; + std::pair(supports_tail, CLAP_EXT_TAIL), + std::pair(supports_voice_info, CLAP_EXT_VOICE_INFO)}; } } // namespace host diff --git a/src/common/serialization/clap/host.h b/src/common/serialization/clap/host.h index ce77ff00..3db658c8 100644 --- a/src/common/serialization/clap/host.h +++ b/src/common/serialization/clap/host.h @@ -89,12 +89,13 @@ struct SupportedHostExtensions { bool supports_params = false; bool supports_state = false; bool supports_tail = false; + bool supports_voice_info = false; /** * Get a list of `` tuples for the supported * extensions. Used during logging. */ - std::array, 8> list() const noexcept; + std::array, 9> list() const noexcept; template void serialize(S& s) { @@ -106,6 +107,7 @@ struct SupportedHostExtensions { s.value1b(supports_params); s.value1b(supports_state); s.value1b(supports_tail); + s.value1b(supports_voice_info); } }; diff --git a/src/common/serialization/clap/plugin.cpp b/src/common/serialization/clap/plugin.cpp index 8ea14d58..f037fa26 100644 --- a/src/common/serialization/clap/plugin.cpp +++ b/src/common/serialization/clap/plugin.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include "version.h" @@ -82,7 +83,7 @@ const clap_plugin_descriptor_t* Descriptor::get() const { return &clap_descriptor; } -std::array, 7> SupportedPluginExtensions::list() +std::array, 8> SupportedPluginExtensions::list() const noexcept { return {std::pair(supports_audio_ports, CLAP_EXT_AUDIO_PORTS), std::pair(supports_gui, CLAP_EXT_GUI), @@ -90,7 +91,8 @@ std::array, 7> SupportedPluginExtensions::list() std::pair(supports_note_ports, CLAP_EXT_NOTE_PORTS), std::pair(supports_params, CLAP_EXT_PARAMS), std::pair(supports_state, CLAP_EXT_STATE), - std::pair(supports_tail, CLAP_EXT_TAIL)}; + std::pair(supports_tail, CLAP_EXT_TAIL), + std::pair(supports_voice_info, CLAP_EXT_VOICE_INFO)}; } } // namespace plugin diff --git a/src/common/serialization/clap/plugin.h b/src/common/serialization/clap/plugin.h index aad11abd..594aa202 100644 --- a/src/common/serialization/clap/plugin.h +++ b/src/common/serialization/clap/plugin.h @@ -122,12 +122,13 @@ struct SupportedPluginExtensions { bool supports_params = false; bool supports_state = false; bool supports_tail = false; + bool supports_voice_info = false; /** * Get a list of `` tuples for the supported * extensions. Used during logging. */ - std::array, 7> list() const noexcept; + std::array, 8> list() const noexcept; template void serialize(S& s) { @@ -138,6 +139,7 @@ struct SupportedPluginExtensions { s.value1b(supports_params); s.value1b(supports_state); s.value1b(supports_tail); + s.value1b(supports_voice_info); } }; diff --git a/src/plugin/bridges/clap-impls/plugin-proxy.cpp b/src/plugin/bridges/clap-impls/plugin-proxy.cpp index dd832bc1..c3000927 100644 --- a/src/plugin/bridges/clap-impls/plugin-proxy.cpp +++ b/src/plugin/bridges/clap-impls/plugin-proxy.cpp @@ -34,7 +34,9 @@ ClapHostExtensions::ClapHostExtensions(const clap_host& host) noexcept state(static_cast( host.get_extension(&host, CLAP_EXT_STATE))), tail(static_cast( - host.get_extension(&host, CLAP_EXT_TAIL))) {} + host.get_extension(&host, CLAP_EXT_TAIL))), + voice_info(static_cast( + host.get_extension(&host, CLAP_EXT_VOICE_INFO))) {} ClapHostExtensions::ClapHostExtensions() noexcept {} @@ -48,7 +50,8 @@ clap::host::SupportedHostExtensions ClapHostExtensions::supported() .supports_note_ports = note_ports != nullptr, .supports_params = params != nullptr, .supports_state = state != nullptr, - .supports_tail = tail != nullptr}; + .supports_tail = tail != nullptr, + .supports_voice_info = voice_info != nullptr}; } clap_plugin_proxy::clap_plugin_proxy(ClapPluginBridge& bridge, @@ -116,6 +119,9 @@ clap_plugin_proxy::clap_plugin_proxy(ClapPluginBridge& bridge, ext_tail_vtable(clap_plugin_tail_t{ .get = ext_tail_get, }), + ext_voice_info_vtable(clap_plugin_voice_info_t{ + .get = ext_voice_info_get, + }), // These function objects are relatively large, and we probably won't be // getting that many of them pending_callbacks_(128) {} @@ -309,6 +315,9 @@ clap_plugin_proxy::plugin_get_extension(const struct clap_plugin* plugin, } else if (self->supported_extensions_.supports_tail && strcmp(id, CLAP_EXT_TAIL) == 0) { extension_ptr = &self->ext_tail_vtable; + } else if (self->supported_extensions_.supports_voice_info && + strcmp(id, CLAP_EXT_VOICE_INFO) == 0) { + extension_ptr = &self->ext_voice_info_vtable; } self->bridge_.logger_.log_extension_query("clap_plugin::get_extension", @@ -757,3 +766,21 @@ uint32_t CLAP_ABI clap_plugin_proxy::ext_tail_get(const clap_plugin_t* plugin) { return self->bridge_.send_audio_thread_message( clap::ext::tail::plugin::Get{.instance_id = self->instance_id()}); } + +bool CLAP_ABI clap_plugin_proxy::ext_voice_info_get(const clap_plugin_t* plugin, + clap_voice_info_t* info) { + assert(plugin && plugin->plugin_data && info); + auto self = static_cast(plugin->plugin_data); + + const clap::ext::voice_info::plugin::GetResponse response = + self->bridge_.send_main_thread_message( + clap::ext::voice_info::plugin::Get{.instance_id = + self->instance_id()}); + if (response.result) { + *info = *response.result; + + return true; + } else { + return false; + } +} diff --git a/src/plugin/bridges/clap-impls/plugin-proxy.h b/src/plugin/bridges/clap-impls/plugin-proxy.h index 1600ffe0..4fc8b1db 100644 --- a/src/plugin/bridges/clap-impls/plugin-proxy.h +++ b/src/plugin/bridges/clap-impls/plugin-proxy.h @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -71,6 +72,7 @@ struct ClapHostExtensions { const clap_host_params_t* params = nullptr; const clap_host_state_t* state = nullptr; const clap_host_tail_t* tail = nullptr; + const clap_host_voice_info_t* voice_info = nullptr; }; /** @@ -263,6 +265,9 @@ class clap_plugin_proxy { static uint32_t CLAP_ABI ext_tail_get(const clap_plugin_t* plugin); + static bool CLAP_ABI ext_voice_info_get(const clap_plugin_t* plugin, + clap_voice_info_t* info); + private: ClapPluginBridge& bridge_; size_t instance_id_; @@ -326,6 +331,7 @@ class clap_plugin_proxy { const clap_plugin_params_t ext_params_vtable; const clap_plugin_state_t ext_state_vtable; const clap_plugin_tail_t ext_tail_vtable; + const clap_plugin_voice_info_t ext_voice_info_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 6e29d112..ce78d86f 100644 --- a/src/plugin/bridges/clap.cpp +++ b/src/plugin/bridges/clap.cpp @@ -259,6 +259,22 @@ ClapPluginBridge::ClapPluginBridge(const ghc::filesystem::path& plugin_path) return Ack{}; }, + [&](const clap::ext::voice_info::host::Changed& request) + -> clap::ext::voice_info::host::Changed::Response { + const auto& [plugin_proxy, _] = + get_proxy(request.owner_instance_id); + + plugin_proxy + .run_on_main_thread( + [&, host = plugin_proxy.host_, + voice_info = + plugin_proxy.host_extensions_.voice_info]() { + voice_info->changed(host); + }) + .wait(); + + 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 fec93ae2..eeee0774 100644 --- a/src/wine-host/bridges/clap-impls/host-proxy.cpp +++ b/src/wine-host/bridges/clap-impls/host-proxy.cpp @@ -84,6 +84,9 @@ clap_host_proxy::clap_host_proxy(ClapBridge& bridge, ext_thread_check_vtable(clap_host_thread_check_t{ .is_main_thread = ext_thread_check_is_main_thread, .is_audio_thread = ext_thread_check_is_audio_thread, + }), + ext_voice_info_vtable(clap_host_voice_info_t{ + .changed = ext_voice_info_changed, }) {} const void* CLAP_ABI @@ -122,6 +125,9 @@ clap_host_proxy::host_get_extension(const struct clap_host* host, } else if (strcmp(extension_id, CLAP_EXT_THREAD_CHECK) == 0) { // This extension doesn't require any bridging extension_ptr = &self->ext_thread_check_vtable; + } else if (self->supported_extensions_.supports_voice_info && + strcmp(extension_id, CLAP_EXT_VOICE_INFO) == 0) { + extension_ptr = &self->ext_voice_info_vtable; } self->bridge_.logger_.log_extension_query("clap_host::get_extension", @@ -432,3 +438,11 @@ clap_host_proxy::ext_thread_check_is_audio_thread(const clap_host_t* host) { // do audio thread stuff on the GUI thread everything's fine return !self->bridge_.main_context_.is_gui_thread(); } + +void CLAP_ABI clap_host_proxy::ext_voice_info_changed(const clap_host_t* host) { + assert(host && host->host_data); + auto self = static_cast(host->host_data); + + self->bridge_.send_main_thread_message(clap::ext::voice_info::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 e191a077..31ad9df4 100644 --- a/src/wine-host/bridges/clap-impls/host-proxy.h +++ b/src/wine-host/bridges/clap-impls/host-proxy.h @@ -27,6 +27,7 @@ #include #include #include +#include #include #include "../../common/serialization/clap/plugin-factory.h" @@ -121,6 +122,8 @@ class clap_host_proxy { static bool CLAP_ABI ext_thread_check_is_audio_thread(const clap_host_t* host); + static void CLAP_ABI ext_voice_info_changed(const clap_host_t* host); + private: ClapBridge& bridge_; size_t owner_instance_id_; @@ -149,6 +152,7 @@ class clap_host_proxy { const clap_host_tail_t ext_tail_vtable; // This is always available regardless of the proxied host const clap_host_thread_check_t ext_thread_check_vtable; + const clap_host_voice_info_t ext_voice_info_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 f1ca2540..e51f3ffe 100644 --- a/src/wine-host/bridges/clap.cpp +++ b/src/wine-host/bridges/clap.cpp @@ -40,7 +40,9 @@ ClapPluginExtensions::ClapPluginExtensions(const clap_plugin& plugin) noexcept state(static_cast( plugin.get_extension(&plugin, CLAP_EXT_STATE))), tail(static_cast( - plugin.get_extension(&plugin, CLAP_EXT_TAIL))) {} + plugin.get_extension(&plugin, CLAP_EXT_TAIL))), + voice_info(static_cast( + plugin.get_extension(&plugin, CLAP_EXT_VOICE_INFO))) {} ClapPluginExtensions::ClapPluginExtensions() noexcept {} @@ -53,7 +55,8 @@ clap::plugin::SupportedPluginExtensions ClapPluginExtensions::supported() .supports_note_ports = note_ports != nullptr, .supports_params = params != nullptr, .supports_state = state != nullptr, - .supports_tail = tail != nullptr}; + .supports_tail = tail != nullptr, + .supports_voice_info = voice_info != nullptr}; } ClapPluginInstance::ClapPluginInstance( @@ -736,6 +739,25 @@ void ClapBridge::run() { }) .get(); }, + [&](clap::ext::voice_info::plugin::Get& request) + -> clap::ext::voice_info::plugin::Get::Response { + const auto& [instance, _] = get_instance(request.instance_id); + + return main_context_ + .run_in_context([&, plugin = instance.plugin.get(), + voice_info = + instance.extensions.voice_info]() { + clap_voice_info_t info{}; + if (voice_info->get(plugin, &info)) { + return clap::ext::voice_info::plugin::GetResponse{ + .result = std::move(info)}; + } else { + return clap::ext::voice_info::plugin::GetResponse{ + .result = std::nullopt}; + } + }) + .get(); + }, }); } diff --git a/src/wine-host/bridges/clap.h b/src/wine-host/bridges/clap.h index a47188a0..b737b7e5 100644 --- a/src/wine-host/bridges/clap.h +++ b/src/wine-host/bridges/clap.h @@ -73,6 +73,7 @@ struct ClapPluginExtensions { const clap_plugin_params_t* params = nullptr; const clap_plugin_state_t* state = nullptr; const clap_plugin_tail_t* tail = nullptr; + const clap_plugin_voice_info_t* voice_info = nullptr; }; /**