diff --git a/src/common/logging/clap.cpp b/src/common/logging/clap.cpp index b3de5f3f..eb651877 100644 --- a/src/common/logging/clap.cpp +++ b/src/common/logging/clap.cpp @@ -83,7 +83,9 @@ bool ClapLogger::log_request(bool is_host_plugin, const auto& supported_extensions = request.supported_host_extensions; for (const auto& [supported, extension_name] : {std::pair(supported_extensions.supports_audio_ports, - CLAP_EXT_AUDIO_PORTS)}) { + CLAP_EXT_AUDIO_PORTS), + std::pair(supported_extensions.supports_note_ports, + CLAP_EXT_NOTE_PORTS)}) { if (!supported) { continue; } @@ -292,7 +294,9 @@ void ClapLogger::log_response(bool is_host_plugin, const auto& supported_extensions = response.supported_plugin_extensions; for (const auto& [supported, extension_name] : {std::pair(supported_extensions.supports_audio_ports, - CLAP_EXT_AUDIO_PORTS)}) { + CLAP_EXT_AUDIO_PORTS), + std::pair(supported_extensions.supports_note_ports, + CLAP_EXT_NOTE_PORTS)}) { if (!supported) { continue; } diff --git a/src/common/serialization/clap.h b/src/common/serialization/clap.h index 9ff66ff8..acf35b1b 100644 --- a/src/common/serialization/clap.h +++ b/src/common/serialization/clap.h @@ -50,7 +50,9 @@ using ClapMainThreadControlRequest = clap::plugin::Activate, clap::plugin::Deactivate, clap::ext::audio_ports::plugin::Count, - clap::ext::audio_ports::plugin::Get>; + clap::ext::audio_ports::plugin::Get, + clap::ext::note_ports::plugin::Count, + clap::ext::note_ports::plugin::Get>; template void serialize(S& s, ClapMainThreadControlRequest& payload) { @@ -134,7 +136,9 @@ using ClapMainThreadCallbackRequest = clap::host::RequestRestart, clap::host::RequestProcess, clap::ext::audio_ports::host::IsRescanFlagSupported, - clap::ext::audio_ports::host::Rescan>; + clap::ext::audio_ports::host::Rescan, + clap::ext::note_ports::host::SupportedDialects, + clap::ext::note_ports::host::Rescan>; template void serialize(S& s, ClapMainThreadCallbackRequest& payload) { diff --git a/src/common/serialization/clap/README.md b/src/common/serialization/clap/README.md index 86aabd19..d4073b67 100644 --- a/src/common/serialization/clap/README.md +++ b/src/common/serialization/clap/README.md @@ -21,7 +21,7 @@ Yabridge currently tracks CLAP 1.1.1. The implementation status for CLAP's core | `clap.latency` | :x: Not supported yet | | `clap.log` | :x: Not supported yet | | `clap.note-name` | :x: Not supported yet | -| `clap.note-ports` | :x: Not supported yet | +| `clap.note-ports` | :heavy_check_mark: | | `clap.params` | :x: Not supported yet | | `clap.posix-fd-support` | :x: Not supported yet | | `clap.render` | :x: Not supported yet | diff --git a/src/common/serialization/clap/host.h b/src/common/serialization/clap/host.h index c3e329fa..9a451961 100644 --- a/src/common/serialization/clap/host.h +++ b/src/common/serialization/clap/host.h @@ -80,12 +80,15 @@ struct Host { * available to the bridged CLAP plugins using proxies. */ struct SupportedHostExtensions { - // Don't forget to add new extensions to the log output + // Don't forget to add new extensions to the logger and to the serialize + // method bool supports_audio_ports = false; + bool supports_note_ports = false; template void serialize(S& s) { s.value1b(supports_audio_ports); + s.value1b(supports_note_ports); } }; diff --git a/src/common/serialization/clap/plugin.h b/src/common/serialization/clap/plugin.h index 124a2347..75b1e302 100644 --- a/src/common/serialization/clap/plugin.h +++ b/src/common/serialization/clap/plugin.h @@ -113,12 +113,15 @@ struct Descriptor { * created by `ClapPluginExtensions::supported()`. */ struct SupportedPluginExtensions { - // Don't forget to add new extensions to the log output + // Don't forget to add new extensions to the logger and to the serialize + // method bool supports_audio_ports = false; + bool supports_note_ports = false; template void serialize(S& s) { s.value1b(supports_audio_ports); + 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 40bd0140..778ec4c5 100644 --- a/src/plugin/bridges/clap-impls/plugin-proxy.cpp +++ b/src/plugin/bridges/clap-impls/plugin-proxy.cpp @@ -20,14 +20,17 @@ ClapHostExtensions::ClapHostExtensions(const clap_host& host) noexcept : audio_ports(static_cast( - host.get_extension(&host, CLAP_EXT_AUDIO_PORTS))) {} + host.get_extension(&host, CLAP_EXT_AUDIO_PORTS))), + note_ports(static_cast( + host.get_extension(&host, CLAP_EXT_NOTE_PORTS))) {} ClapHostExtensions::ClapHostExtensions() noexcept {} clap::host::SupportedHostExtensions ClapHostExtensions::supported() const noexcept { - return clap::host::SupportedHostExtensions{.supports_audio_ports = - audio_ports != nullptr}; + return clap::host::SupportedHostExtensions{ + .supports_audio_ports = audio_ports != nullptr, + .supports_note_ports = note_ports != nullptr}; } clap_plugin_proxy::clap_plugin_proxy(ClapPluginBridge& bridge, @@ -56,6 +59,10 @@ clap_plugin_proxy::clap_plugin_proxy(ClapPluginBridge& bridge, .count = ext_audio_ports_count, .get = ext_audio_ports_get, }), + ext_note_ports_vtable(clap_plugin_note_ports_t{ + .count = ext_note_ports_count, + .get = ext_note_ports_get, + }), // These function objects are relatively large, and we probably won't be // getting that many of them pending_callbacks_(128) {} @@ -178,6 +185,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_note_ports && + strcmp(id, CLAP_EXT_NOTE_PORTS) == 0) { + extension_ptr = &self->ext_note_ports_vtable; } self->bridge_.logger_.log_extension_query("clap_plugin::get_extension", @@ -232,3 +242,37 @@ clap_plugin_proxy::ext_audio_ports_get(const clap_plugin_t* plugin, return false; } } + +uint32_t CLAP_ABI +clap_plugin_proxy::ext_note_ports_count(const clap_plugin_t* plugin, + bool is_input) { + assert(plugin && plugin->plugin_data); + auto self = static_cast(plugin->plugin_data); + + return self->bridge_.send_main_thread_message( + clap::ext::note_ports::plugin::Count{.instance_id = self->instance_id(), + .is_input = is_input}); +} + +bool CLAP_ABI +clap_plugin_proxy::ext_note_ports_get(const clap_plugin_t* plugin, + uint32_t index, + bool is_input, + clap_note_port_info_t* info) { + assert(plugin && plugin->plugin_data && info); + auto self = static_cast(plugin->plugin_data); + + const clap::ext::note_ports::plugin::GetResponse response = + self->bridge_.send_main_thread_message( + clap::ext::note_ports::plugin::Get{ + .instance_id = self->instance_id(), + .index = index, + .is_input = is_input}); + if (response.result) { + response.result->reconstruct(*info); + + 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 9039cdbe..5854686f 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 @@ -56,6 +57,7 @@ struct ClapHostExtensions { clap::host::SupportedHostExtensions supported() const noexcept; const clap_host_audio_ports_t* audio_ports = nullptr; + const clap_host_note_ports_t* note_ports = nullptr; }; /** @@ -130,6 +132,13 @@ class clap_plugin_proxy { bool is_input, clap_audio_port_info_t* info); + static uint32_t CLAP_ABI ext_note_ports_count(const clap_plugin_t* plugin, + bool is_input); + static bool CLAP_ABI ext_note_ports_get(const clap_plugin_t* plugin, + uint32_t index, + bool is_input, + clap_note_port_info_t* info); + /** * Asynchronously run a function on the host's main thread, returning the * result as a future. @@ -186,6 +195,7 @@ class clap_plugin_proxy { // 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; /** * 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 76c29f74..28bd8ea0 100644 --- a/src/plugin/bridges/clap.cpp +++ b/src/plugin/bridges/clap.cpp @@ -111,6 +111,39 @@ ClapPluginBridge::ClapPluginBridge(const ghc::filesystem::path& plugin_path) return Ack{}; }, + [&](const clap::ext::note_ports::host::SupportedDialects& + request) + -> clap::ext::note_ports::host::SupportedDialects:: + Response { + const auto& [plugin_proxy, _] = + get_proxy(request.owner_instance_id); + + return plugin_proxy + .run_on_main_thread( + [host = plugin_proxy.host_, + note_ports = plugin_proxy.extensions_ + .note_ports]() { + return note_ports->supported_dialects( + host); + }) + .get(); + }, + [&](const clap::ext::note_ports::host::Rescan& request) + -> clap::ext::note_ports::host::Rescan::Response { + const auto& [plugin_proxy, _] = + get_proxy(request.owner_instance_id); + + plugin_proxy + .run_on_main_thread( + [&, host = plugin_proxy.host_, + note_ports = + plugin_proxy.extensions_.note_ports]() { + note_ports->rescan(host, request.flags); + }) + .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 c00dd605..2339bfca 100644 --- a/src/wine-host/bridges/clap-impls/host-proxy.cpp +++ b/src/wine-host/bridges/clap-impls/host-proxy.cpp @@ -47,6 +47,10 @@ clap_host_proxy::clap_host_proxy(ClapBridge& bridge, ext_audio_ports_vtable(clap_host_audio_ports_t{ .is_rescan_flag_supported = ext_audio_ports_is_rescan_flag_supported, .rescan = ext_audio_ports_rescan, + }), + ext_note_ports_vtable(clap_host_note_ports_t{ + .supported_dialects = ext_note_ports_supported_dialects, + .rescan = ext_note_ports_rescan, }) {} const void* CLAP_ABI @@ -135,3 +139,22 @@ void CLAP_ABI clap_host_proxy::ext_audio_ports_rescan(const clap_host_t* host, self->bridge_.send_main_thread_message(clap::ext::audio_ports::host::Rescan{ .owner_instance_id = self->owner_instance_id(), .flags = flags}); } + +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); + + return self->bridge_.send_main_thread_message( + clap::ext::note_ports::host::SupportedDialects{ + .owner_instance_id = self->owner_instance_id()}); +} + +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); + + self->bridge_.send_main_thread_message(clap::ext::note_ports::host::Rescan{ + .owner_instance_id = self->owner_instance_id(), .flags = flags}); +} diff --git a/src/wine-host/bridges/clap-impls/host-proxy.h b/src/wine-host/bridges/clap-impls/host-proxy.h index d92c3792..026f107c 100644 --- a/src/wine-host/bridges/clap-impls/host-proxy.h +++ b/src/wine-host/bridges/clap-impls/host-proxy.h @@ -19,6 +19,7 @@ #include #include +#include #include #include "../../common/serialization/clap/plugin-factory.h" @@ -69,6 +70,11 @@ class clap_host_proxy { static void CLAP_ABI ext_audio_ports_rescan(const clap_host_t* host, uint32_t flags); + static uint32_t CLAP_ABI + ext_note_ports_supported_dialects(const clap_host_t* host); + static void CLAP_ABI ext_note_ports_rescan(const clap_host_t* host, + uint32_t flags); + /** * The extensions supported by the host, set just before calling * `clap_plugin::init()` on the bridged plugin. We'll allow the plugin to @@ -92,6 +98,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 ext_audio_ports_vtable; + const clap_host_note_ports ext_note_ports_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 06eb55be..dbb2a9a2 100644 --- a/src/wine-host/bridges/clap.cpp +++ b/src/wine-host/bridges/clap.cpp @@ -28,14 +28,17 @@ namespace fs = ghc::filesystem; ClapPluginExtensions::ClapPluginExtensions(const clap_plugin& plugin) noexcept : audio_ports(static_cast( - plugin.get_extension(&plugin, CLAP_EXT_AUDIO_PORTS))) {} + plugin.get_extension(&plugin, CLAP_EXT_AUDIO_PORTS))), + note_ports(static_cast( + plugin.get_extension(&plugin, CLAP_EXT_NOTE_PORTS))) {} ClapPluginExtensions::ClapPluginExtensions() noexcept {} clap::plugin::SupportedPluginExtensions ClapPluginExtensions::supported() const noexcept { - return clap::plugin::SupportedPluginExtensions{.supports_audio_ports = - audio_ports != nullptr}; + return clap::plugin::SupportedPluginExtensions{ + .supports_audio_ports = audio_ports != nullptr, + .supports_note_ports = note_ports != nullptr}; } ClapPluginInstance::ClapPluginInstance( @@ -334,6 +337,30 @@ void ClapBridge::run() { .result = std::nullopt}; } }, + [&](const clap::ext::note_ports::plugin::Count& request) + -> clap::ext::note_ports::plugin::Count::Response { + const auto& [instance, _] = get_instance(request.instance_id); + + return instance.extensions.note_ports->count( + instance.plugin.get(), request.is_input); + }, + [&](const clap::ext::note_ports::plugin::Get& request) + -> clap::ext::note_ports::plugin::Get::Response { + const auto& [instance, _] = get_instance(request.instance_id); + + // We'll also ignore the main thread requirement here for + // performance's sake + clap_note_port_info_t info{}; + if (instance.extensions.note_ports->get( + instance.plugin.get(), request.index, request.is_input, + &info)) { + return clap::ext::note_ports::plugin::GetResponse{.result = + info}; + } else { + return clap::ext::note_ports::plugin::GetResponse{ + .result = std::nullopt}; + } + }, }); } diff --git a/src/wine-host/bridges/clap.h b/src/wine-host/bridges/clap.h index be6d8fec..73547776 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_note_ports_t* note_ports = nullptr; }; /**