From 6cb12aad7357c84354e62b8d1b2ba2a57bc738ad Mon Sep 17 00:00:00 2001 From: Robbert van der Helm Date: Sun, 9 Oct 2022 00:21:29 +0200 Subject: [PATCH] Implement the CLAP log extension --- src/common/serialization/clap.h | 1 + src/common/serialization/clap/README.md | 38 ++++++------ src/common/serialization/clap/ext/log.h | 2 +- src/common/serialization/clap/host.cpp | 4 +- src/common/serialization/clap/host.h | 4 +- .../bridges/clap-impls/plugin-proxy.cpp | 3 + src/plugin/bridges/clap-impls/plugin-proxy.h | 2 + src/plugin/bridges/clap.cpp | 11 ++++ .../bridges/clap-impls/host-proxy.cpp | 61 +++++++++++++++++++ src/wine-host/bridges/clap-impls/host-proxy.h | 10 +++ 10 files changed, 114 insertions(+), 22 deletions(-) diff --git a/src/common/serialization/clap.h b/src/common/serialization/clap.h index 4a01d97d..1666ff37 100644 --- a/src/common/serialization/clap.h +++ b/src/common/serialization/clap.h @@ -199,6 +199,7 @@ void serialize(S& s, ClapMainThreadCallbackRequest& payload) { */ using ClapAudioThreadCallbackRequest = std::variant; diff --git a/src/common/serialization/clap/README.md b/src/common/serialization/clap/README.md index a9323c9a..8d7d048d 100644 --- a/src/common/serialization/clap/README.md +++ b/src/common/serialization/clap/README.md @@ -15,25 +15,25 @@ Yabridge currently tracks CLAP 1.1.1. The implementation status for CLAP's core | `clap.plugin-factory` | :heavy_check_mark: | | `clap.plugin-invalidation-factory/draft0` | :x: Not supported yet | -| extension | status | -| ------------------------- | ---------------------------------------------------- | -| `clap.audio-ports` | :heavy_check_mark: | -| `clap.audio-ports-config` | :x: Not supported yet | -| `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: | -| `clap.log` | :x: Not supported yet | -| `clap.note-name` | :x: Not supported yet | -| `clap.note-ports` | :heavy_check_mark: | -| `clap.params` | :heavy_check_mark: | -| `clap.posix-fd-support` | :x: Not supported yet | -| `clap.render` | :x: Not supported yet | -| `clap.state` | :heavy_check_mark: | -| `clap.tail` | :heavy_check_mark: | -| `clap.thread-check` | :heavy_check_mark: | -| `clap.thread-pool` | :x: Not supported yet | -| `clap.timer-support` | :x: Not supported yet | -| `clap.voice-info` | :x: Not supported yet | +| extension | status | +| ------------------------- | ------------------------------------------------------------ | +| `clap.audio-ports` | :heavy_check_mark: | +| `clap.audio-ports-config` | :x: Not supported yet | +| `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: | +| `clap.log` | :heavy_check_mark: Always supported with or without bridging | +| `clap.note-name` | :x: Not supported yet | +| `clap.note-ports` | :heavy_check_mark: | +| `clap.params` | :heavy_check_mark: | +| `clap.posix-fd-support` | :x: Not supported yet | +| `clap.render` | :x: Not supported yet | +| `clap.state` | :heavy_check_mark: | +| `clap.tail` | :heavy_check_mark: | +| `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 | | draft extension | status | | -------------------------------- | --------------------- | diff --git a/src/common/serialization/clap/ext/log.h b/src/common/serialization/clap/ext/log.h index a35e0579..a4954e83 100644 --- a/src/common/serialization/clap/ext/log.h +++ b/src/common/serialization/clap/ext/log.h @@ -45,7 +45,7 @@ struct Log { void serialize(S& s) { s.value8b(owner_instance_id); s.value4b(severity); - s.text(msg, 1 << 16); + s.text1b(msg, 1 << 16); } }; diff --git a/src/common/serialization/clap/host.cpp b/src/common/serialization/clap/host.cpp index 0fa7a200..096a769f 100644 --- a/src/common/serialization/clap/host.cpp +++ b/src/common/serialization/clap/host.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -34,11 +35,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, 7> SupportedHostExtensions::list() +std::array, 8> SupportedHostExtensions::list() const noexcept { return {std::pair(supports_audio_ports, CLAP_EXT_AUDIO_PORTS), std::pair(supports_gui, CLAP_EXT_GUI), std::pair(supports_latency, CLAP_EXT_LATENCY), + std::pair(supports_log, CLAP_EXT_LOG), std::pair(supports_note_ports, CLAP_EXT_NOTE_PORTS), std::pair(supports_params, CLAP_EXT_PARAMS), std::pair(supports_state, CLAP_EXT_STATE), diff --git a/src/common/serialization/clap/host.h b/src/common/serialization/clap/host.h index 033ff299..ce77ff00 100644 --- a/src/common/serialization/clap/host.h +++ b/src/common/serialization/clap/host.h @@ -84,6 +84,7 @@ struct SupportedHostExtensions { bool supports_audio_ports = false; bool supports_gui = false; bool supports_latency = false; + bool supports_log = false; bool supports_note_ports = false; bool supports_params = false; bool supports_state = false; @@ -93,13 +94,14 @@ struct SupportedHostExtensions { * 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) { s.value1b(supports_audio_ports); s.value1b(supports_gui); s.value1b(supports_latency); + s.value1b(supports_log); s.value1b(supports_note_ports); s.value1b(supports_params); s.value1b(supports_state); diff --git a/src/plugin/bridges/clap-impls/plugin-proxy.cpp b/src/plugin/bridges/clap-impls/plugin-proxy.cpp index 315e0715..dd832bc1 100644 --- a/src/plugin/bridges/clap-impls/plugin-proxy.cpp +++ b/src/plugin/bridges/clap-impls/plugin-proxy.cpp @@ -25,6 +25,8 @@ ClapHostExtensions::ClapHostExtensions(const clap_host& host) noexcept host.get_extension(&host, CLAP_EXT_GUI))), latency(static_cast( host.get_extension(&host, CLAP_EXT_LATENCY))), + log(static_cast( + host.get_extension(&host, CLAP_EXT_LOG))), note_ports(static_cast( host.get_extension(&host, CLAP_EXT_NOTE_PORTS))), params(static_cast( @@ -42,6 +44,7 @@ clap::host::SupportedHostExtensions ClapHostExtensions::supported() .supports_audio_ports = audio_ports != nullptr, .supports_gui = gui != nullptr, .supports_latency = latency != nullptr, + .supports_log = log != nullptr, .supports_note_ports = note_ports != nullptr, .supports_params = params != nullptr, .supports_state = state != nullptr, diff --git a/src/plugin/bridges/clap-impls/plugin-proxy.h b/src/plugin/bridges/clap-impls/plugin-proxy.h index a96b96f2..1600ffe0 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 @@ -65,6 +66,7 @@ struct ClapHostExtensions { const clap_host_audio_ports_t* audio_ports = nullptr; const clap_host_gui_t* gui = nullptr; const clap_host_latency_t* latency = nullptr; + const clap_host_log_t* log = nullptr; const clap_host_note_ports_t* note_ports = nullptr; const clap_host_params_t* params = nullptr; const clap_host_state_t* state = nullptr; diff --git a/src/plugin/bridges/clap.cpp b/src/plugin/bridges/clap.cpp index 7a524608..6e29d112 100644 --- a/src/plugin/bridges/clap.cpp +++ b/src/plugin/bridges/clap.cpp @@ -346,6 +346,17 @@ void ClapPluginBridge::register_plugin_proxy( // hell. I haven't been able to figure out why. return {}; }, + [&](const clap::ext::log::host::Log& request) + -> clap::ext::log::host::Log::Response { + const auto& [plugin_proxy, _] = + get_proxy(request.owner_instance_id); + + plugin_proxy.host_extensions_.log->log(plugin_proxy.host_, + request.severity, + request.msg.c_str()); + + return Ack{}; + }, [&](const clap::ext::params::host::RequestFlush& request) -> clap::ext::params::host::RequestFlush::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 4f34e463..d83bbe9b 100644 --- a/src/wine-host/bridges/clap-impls/host-proxy.cpp +++ b/src/wine-host/bridges/clap-impls/host-proxy.cpp @@ -63,6 +63,9 @@ clap_host_proxy::clap_host_proxy(ClapBridge& bridge, ext_latency_vtable(clap_host_latency_t{ .changed = ext_latency_changed, }), + ext_log_vtable(clap_host_log_t{ + .log = ext_log_log, + }), ext_note_ports_vtable(clap_host_note_ports_t{ .supported_dialects = ext_note_ports_supported_dialects, .rescan = ext_note_ports_rescan, @@ -99,6 +102,11 @@ clap_host_proxy::host_get_extension(const struct clap_host* host, } else if (self->supported_extensions_.supports_latency && strcmp(extension_id, CLAP_EXT_LATENCY) == 0) { extension_ptr = &self->ext_latency_vtable; + } else if (strcmp(extension_id, CLAP_EXT_LOG) == 0) { + // This extension is always supported. We'll bridge it if the host also + // supports it, or we'll print the message if it doesn't. That allows us + // to filter misbehavior messages. + extension_ptr = &self->ext_log_vtable; } else if (self->supported_extensions_.supports_note_ports && strcmp(extension_id, CLAP_EXT_NOTE_PORTS) == 0) { extension_ptr = &self->ext_note_ports_vtable; @@ -265,6 +273,59 @@ void CLAP_ABI clap_host_proxy::ext_latency_changed(const clap_host_t* host) { self->owner_instance_id()}); } +void CLAP_ABI clap_host_proxy::ext_log_log(const clap_host_t* host, + clap_log_severity severity, + const char* msg) { + assert(host && host->host_data && msg); + auto self = static_cast(host->host_data); + + // We'll always support this extension, even if the host doesn't. That + // allows us to filter misbehavior messages from the CLAP helper. + if ((severity == CLAP_LOG_HOST_MISBEHAVING || + severity == CLAP_LOG_PLUGIN_MISBEHAVING) && + self->bridge_.logger_.verbosity() < Logger::Verbosity::all_events) { + return; + } + + // We'll bridge this if possible, otherwise we'll just print the message to + // the logger (through STDERR) + if (self->supported_extensions_.supports_log) { + self->bridge_.send_audio_thread_message(clap::ext::log::host::Log{ + .owner_instance_id = self->owner_instance_id(), + .severity = severity, + .msg = msg}); + } else { + switch (severity) { + case CLAP_LOG_DEBUG: + std::cerr << "[DEBUG] "; + break; + case CLAP_LOG_INFO: + std::cerr << "[INFO] "; + break; + case CLAP_LOG_WARNING: + std::cerr << "[WARNING] "; + break; + case CLAP_LOG_ERROR: + std::cerr << "[ERROR] "; + break; + case CLAP_LOG_FATAL: + std::cerr << "[FATAL] "; + break; + case CLAP_LOG_HOST_MISBEHAVING: + std::cerr << "[HOST_MISBEHAVING] "; + break; + case CLAP_LOG_PLUGIN_MISBEHAVING: + std::cerr << "[PLUGIN_MISBEHAVING] "; + break; + default: + std::cerr << "[unknown log level " << severity << "] "; + break; + } + + std::cerr << msg << std::endl; + } +} + uint32_t CLAP_ABI clap_host_proxy::ext_note_ports_supported_dialects(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 520d9cb2..e191a077 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 #include @@ -95,6 +96,10 @@ class clap_host_proxy { static void CLAP_ABI ext_latency_changed(const clap_host_t* host); + static void CLAP_ABI ext_log_log(const clap_host_t* host, + clap_log_severity severity, + const char* msg); + 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, @@ -134,10 +139,15 @@ class clap_host_proxy { const clap_host_audio_ports_t ext_audio_ports_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 + // can filter out plugin/host misbehavior messages on lower yabridge + // verbosity levels. + const clap_host_log_t ext_log_vtable; const clap_host_note_ports_t ext_note_ports_vtable; const clap_host_params_t ext_params_vtable; const clap_host_state_t ext_state_vtable; 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; /**