mirror of
https://github.com/robbert-vdh/yabridge.git
synced 2026-05-06 19:40:10 +02:00
Allow mutual recursion on all CLAP callbacks
This commit is contained in:
@@ -128,6 +128,10 @@ class clap_plugin_proxy {
|
||||
/**
|
||||
* Asynchronously run a function on the host's main thread, returning the
|
||||
* result as a future.
|
||||
*
|
||||
* Instead of calling this directly, `ClapBridge::run_on_main_thread()`
|
||||
* should be used instead. That also handles mutually recursive main thread
|
||||
* callbacks.
|
||||
*/
|
||||
template <std::invocable F>
|
||||
std::future<std::invoke_result_t<F>> run_on_main_thread(F&& fn) {
|
||||
|
||||
+89
-88
@@ -60,11 +60,10 @@ ClapPluginBridge::ClapPluginBridge(const ghc::filesystem::path& plugin_path)
|
||||
const auto& [plugin_proxy, _] =
|
||||
get_proxy(request.owner_instance_id);
|
||||
|
||||
plugin_proxy
|
||||
.run_on_main_thread([host = plugin_proxy.host_]() {
|
||||
host->request_restart(host);
|
||||
})
|
||||
.wait();
|
||||
run_on_main_thread(plugin_proxy, [host = plugin_proxy
|
||||
.host_]() {
|
||||
host->request_restart(host);
|
||||
}).wait();
|
||||
|
||||
return Ack{};
|
||||
},
|
||||
@@ -73,11 +72,10 @@ ClapPluginBridge::ClapPluginBridge(const ghc::filesystem::path& plugin_path)
|
||||
const auto& [plugin_proxy, _] =
|
||||
get_proxy(request.owner_instance_id);
|
||||
|
||||
plugin_proxy
|
||||
.run_on_main_thread([host = plugin_proxy.host_]() {
|
||||
host->request_process(host);
|
||||
})
|
||||
.wait();
|
||||
run_on_main_thread(plugin_proxy, [host = plugin_proxy
|
||||
.host_]() {
|
||||
host->request_process(host);
|
||||
}).wait();
|
||||
|
||||
return Ack{};
|
||||
},
|
||||
@@ -88,15 +86,16 @@ ClapPluginBridge::ClapPluginBridge(const ghc::filesystem::path& plugin_path)
|
||||
const auto& [plugin_proxy, _] =
|
||||
get_proxy(request.owner_instance_id);
|
||||
|
||||
return plugin_proxy
|
||||
.run_on_main_thread(
|
||||
[&, host = plugin_proxy.host_,
|
||||
audio_ports = plugin_proxy.host_extensions_
|
||||
.audio_ports]() {
|
||||
return audio_ports
|
||||
->is_rescan_flag_supported(
|
||||
host, request.flag);
|
||||
})
|
||||
return run_on_main_thread(
|
||||
plugin_proxy,
|
||||
[&, host = plugin_proxy.host_,
|
||||
audio_ports =
|
||||
plugin_proxy.host_extensions_
|
||||
.audio_ports]() {
|
||||
return audio_ports
|
||||
->is_rescan_flag_supported(
|
||||
host, request.flag);
|
||||
})
|
||||
.get();
|
||||
},
|
||||
[&](const clap::ext::audio_ports::host::Rescan& request)
|
||||
@@ -104,13 +103,13 @@ ClapPluginBridge::ClapPluginBridge(const ghc::filesystem::path& plugin_path)
|
||||
const auto& [plugin_proxy, _] =
|
||||
get_proxy(request.owner_instance_id);
|
||||
|
||||
plugin_proxy
|
||||
.run_on_main_thread(
|
||||
[&, host = plugin_proxy.host_,
|
||||
audio_ports =
|
||||
plugin_proxy.host_extensions_.audio_ports]() {
|
||||
audio_ports->rescan(host, request.flags);
|
||||
})
|
||||
run_on_main_thread(
|
||||
plugin_proxy,
|
||||
[&, host = plugin_proxy.host_,
|
||||
audio_ports =
|
||||
plugin_proxy.host_extensions_.audio_ports]() {
|
||||
audio_ports->rescan(host, request.flags);
|
||||
})
|
||||
.wait();
|
||||
|
||||
return Ack{};
|
||||
@@ -120,13 +119,13 @@ ClapPluginBridge::ClapPluginBridge(const ghc::filesystem::path& plugin_path)
|
||||
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);
|
||||
})
|
||||
run_on_main_thread(
|
||||
plugin_proxy,
|
||||
[&, host = plugin_proxy.host_,
|
||||
audio_ports_config = plugin_proxy.host_extensions_
|
||||
.audio_ports_config]() {
|
||||
audio_ports_config->rescan(host);
|
||||
})
|
||||
.wait();
|
||||
|
||||
return Ack{};
|
||||
@@ -185,13 +184,12 @@ ClapPluginBridge::ClapPluginBridge(const ghc::filesystem::path& plugin_path)
|
||||
const auto& [plugin_proxy, _] =
|
||||
get_proxy(request.owner_instance_id);
|
||||
|
||||
plugin_proxy
|
||||
.run_on_main_thread(
|
||||
[&, host = plugin_proxy.host_,
|
||||
latency =
|
||||
plugin_proxy.host_extensions_.latency]() {
|
||||
latency->changed(host);
|
||||
})
|
||||
run_on_main_thread(
|
||||
plugin_proxy,
|
||||
[&, host = plugin_proxy.host_,
|
||||
latency = plugin_proxy.host_extensions_.latency]() {
|
||||
latency->changed(host);
|
||||
})
|
||||
.wait();
|
||||
|
||||
return Ack{};
|
||||
@@ -201,13 +199,13 @@ ClapPluginBridge::ClapPluginBridge(const ghc::filesystem::path& plugin_path)
|
||||
const auto& [plugin_proxy, _] =
|
||||
get_proxy(request.owner_instance_id);
|
||||
|
||||
plugin_proxy
|
||||
.run_on_main_thread(
|
||||
[&, host = plugin_proxy.host_,
|
||||
note_name =
|
||||
plugin_proxy.host_extensions_.note_name]() {
|
||||
note_name->changed(host);
|
||||
})
|
||||
run_on_main_thread(
|
||||
plugin_proxy,
|
||||
[&, host = plugin_proxy.host_,
|
||||
note_name =
|
||||
plugin_proxy.host_extensions_.note_name]() {
|
||||
note_name->changed(host);
|
||||
})
|
||||
.wait();
|
||||
|
||||
return Ack{};
|
||||
@@ -219,14 +217,15 @@ ClapPluginBridge::ClapPluginBridge(const ghc::filesystem::path& plugin_path)
|
||||
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.host_extensions_
|
||||
.note_ports]() {
|
||||
return note_ports->supported_dialects(
|
||||
host);
|
||||
})
|
||||
return run_on_main_thread(
|
||||
plugin_proxy,
|
||||
[host = plugin_proxy.host_,
|
||||
note_ports =
|
||||
plugin_proxy.host_extensions_
|
||||
.note_ports]() {
|
||||
return note_ports
|
||||
->supported_dialects(host);
|
||||
})
|
||||
.get();
|
||||
},
|
||||
[&](const clap::ext::note_ports::host::Rescan& request)
|
||||
@@ -234,13 +233,13 @@ ClapPluginBridge::ClapPluginBridge(const ghc::filesystem::path& plugin_path)
|
||||
const auto& [plugin_proxy, _] =
|
||||
get_proxy(request.owner_instance_id);
|
||||
|
||||
plugin_proxy
|
||||
.run_on_main_thread(
|
||||
[&, host = plugin_proxy.host_,
|
||||
note_ports =
|
||||
plugin_proxy.host_extensions_.note_ports]() {
|
||||
note_ports->rescan(host, request.flags);
|
||||
})
|
||||
run_on_main_thread(
|
||||
plugin_proxy,
|
||||
[&, host = plugin_proxy.host_,
|
||||
note_ports =
|
||||
plugin_proxy.host_extensions_.note_ports]() {
|
||||
note_ports->rescan(host, request.flags);
|
||||
})
|
||||
.wait();
|
||||
|
||||
return Ack{};
|
||||
@@ -250,12 +249,14 @@ ClapPluginBridge::ClapPluginBridge(const ghc::filesystem::path& plugin_path)
|
||||
const auto& [plugin_proxy, _] =
|
||||
get_proxy(request.owner_instance_id);
|
||||
|
||||
plugin_proxy
|
||||
.run_on_main_thread(
|
||||
[&, host = plugin_proxy.host_,
|
||||
params = plugin_proxy.host_extensions_.params]() {
|
||||
params->rescan(host, request.flags);
|
||||
})
|
||||
// TODO: Handle mutual recursion here and for latency
|
||||
// changes
|
||||
run_on_main_thread(
|
||||
plugin_proxy,
|
||||
[&, host = plugin_proxy.host_,
|
||||
params = plugin_proxy.host_extensions_.params]() {
|
||||
params->rescan(host, request.flags);
|
||||
})
|
||||
.wait();
|
||||
|
||||
return Ack{};
|
||||
@@ -265,13 +266,13 @@ ClapPluginBridge::ClapPluginBridge(const ghc::filesystem::path& plugin_path)
|
||||
const auto& [plugin_proxy, _] =
|
||||
get_proxy(request.owner_instance_id);
|
||||
|
||||
plugin_proxy
|
||||
.run_on_main_thread(
|
||||
[&, host = plugin_proxy.host_,
|
||||
params = plugin_proxy.host_extensions_.params]() {
|
||||
params->clear(host, request.param_id,
|
||||
request.flags);
|
||||
})
|
||||
run_on_main_thread(
|
||||
plugin_proxy,
|
||||
[&, host = plugin_proxy.host_,
|
||||
params = plugin_proxy.host_extensions_.params]() {
|
||||
params->clear(host, request.param_id,
|
||||
request.flags);
|
||||
})
|
||||
.wait();
|
||||
|
||||
return Ack{};
|
||||
@@ -281,12 +282,12 @@ ClapPluginBridge::ClapPluginBridge(const ghc::filesystem::path& plugin_path)
|
||||
const auto& [plugin_proxy, _] =
|
||||
get_proxy(request.owner_instance_id);
|
||||
|
||||
plugin_proxy
|
||||
.run_on_main_thread(
|
||||
[&, host = plugin_proxy.host_,
|
||||
state = plugin_proxy.host_extensions_.state]() {
|
||||
state->mark_dirty(host);
|
||||
})
|
||||
run_on_main_thread(
|
||||
plugin_proxy,
|
||||
[&, host = plugin_proxy.host_,
|
||||
state = plugin_proxy.host_extensions_.state]() {
|
||||
state->mark_dirty(host);
|
||||
})
|
||||
.wait();
|
||||
|
||||
return Ack{};
|
||||
@@ -296,13 +297,13 @@ ClapPluginBridge::ClapPluginBridge(const ghc::filesystem::path& plugin_path)
|
||||
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);
|
||||
})
|
||||
run_on_main_thread(
|
||||
plugin_proxy,
|
||||
[&, host = plugin_proxy.host_,
|
||||
voice_info =
|
||||
plugin_proxy.host_extensions_.voice_info]() {
|
||||
voice_info->changed(host);
|
||||
})
|
||||
.wait();
|
||||
|
||||
return Ack{};
|
||||
|
||||
+81
-46
@@ -142,45 +142,81 @@ class ClapPluginBridge : PluginBridge<ClapSockets<std::jthread>> {
|
||||
}
|
||||
|
||||
// TODO: Do we need this for CLAP? If we do, update the docstring
|
||||
// /**
|
||||
// * Send a message, and allow other threads to call functions on _this
|
||||
// * thread_ while we're waiting for a response. This lets us execute
|
||||
// * functions from the host's GUI thread while it is also calling
|
||||
// functions
|
||||
// * from that same thread. Because of that, we also know that while this
|
||||
// * function is being called the host won't be able to handle any
|
||||
// `IRunLoop`
|
||||
// * events. We need this to support REAPER, because REAPER requires
|
||||
// function
|
||||
// * calls involving the GUI to be run from the GUI thread. Grep for
|
||||
// * `run_gui_task` for instances of this.
|
||||
// *
|
||||
// * We use the same trick in `ClapBridge`.
|
||||
// */
|
||||
// template <typename T>
|
||||
// typename T::Response send_mutually_recursive_message(const T& object) {
|
||||
// return mutual_recursion_.fork([&]() { return send_message(object);
|
||||
// });
|
||||
// }
|
||||
/**
|
||||
* Send a message meant to be executed on the main thread, and allow other
|
||||
* threads to call functions on _this thread_ while we're waiting for a
|
||||
* response. This lets us execute functions from the host's main thread
|
||||
* while it is also calling functions from that same thread. Because of
|
||||
* that, we also know that while this function is being called the host
|
||||
* won't be able to handle any `clap_host::request_callback()` requests. We
|
||||
* need this for a couple situations, like a plugin calling
|
||||
* `clap_host_*::rescan()` during state loading.
|
||||
*
|
||||
* We use the same trick in `ClapBridge`.
|
||||
*/
|
||||
template <typename T>
|
||||
typename T::Response send_mutually_recursive_main_thread_message(
|
||||
const T& object) {
|
||||
return mutual_recursion_.fork(
|
||||
[&]() { return send_main_thread_message(object); });
|
||||
}
|
||||
|
||||
// /**
|
||||
// * If `send_mutually_recursive_message()` is currently being called, then
|
||||
// * run `fn` on the thread that's currently calling that function and
|
||||
// return
|
||||
// * the result of the call. If there's currently no mutually recursive
|
||||
// * function call going on, this will return an `std::nullopt`, and the
|
||||
// * caller should call `fn` itself.
|
||||
// *
|
||||
// * @return The result of calling `fn`, if `fn` was called.
|
||||
// *
|
||||
// * @see ClapPlugViewProxyImpl::run_gui_task
|
||||
// */
|
||||
// template <std::invocable F>
|
||||
// std::optional<std::invoke_result_t<F>>
|
||||
// maybe_run_on_mutual_recursion_thread(
|
||||
// F&& fn) {
|
||||
// return mutual_recursion_.maybe_handle(std::forward<F>(fn));
|
||||
// }
|
||||
/**
|
||||
* Run a callback on the host's GUI thread.
|
||||
*
|
||||
* If `send_mutually_recursive_main_thread_message()` is currently being
|
||||
* called, then run `fn` on the thread that's currently calling that
|
||||
* function and return the result of the call.
|
||||
*
|
||||
* Otherwise, use `clap_plugin_proxy::run_on_main_thread()` to use CLAP's
|
||||
* `clap_plugin::request_callback()` mechanic.
|
||||
*
|
||||
* @return The result of calling `fn`
|
||||
*
|
||||
* @see clap_plugin_proxy::run_on_main_thread
|
||||
*/
|
||||
template <std::invocable F>
|
||||
std::future<std::invoke_result_t<F>> run_on_main_thread(
|
||||
clap_plugin_proxy& plugin,
|
||||
F&& fn) {
|
||||
using Result = std::invoke_result_t<F>;
|
||||
|
||||
// If `ClapBridge::send_mutually_recursive_main_thread_message()` is
|
||||
// currently being called, then we'll call `fn` from that same thread.
|
||||
// Otherwise we'll schedule the task to be run using the host's main
|
||||
// thread using `clap_host::request_callback()`. This is needed because
|
||||
// `request_callback()` won't do anything if that thread is currently
|
||||
// blocked.
|
||||
|
||||
// Modifying the `mutual_recursion_` methods to handle `void` correctly
|
||||
// would lead to a lot more template soup, so we'll just work around it
|
||||
// here.
|
||||
// TODO: At some point, do improve the API so it can handle void without
|
||||
// workaorunds
|
||||
if constexpr (std::is_void_v<Result>) {
|
||||
if (const auto result =
|
||||
mutual_recursion_.maybe_handle([f = std::forward<F>(fn)]() {
|
||||
f();
|
||||
return Ack{};
|
||||
})) {
|
||||
// Apparently there's no way to just create a ready future
|
||||
std::promise<void> result_promise;
|
||||
result_promise.set_value();
|
||||
|
||||
return result_promise.get_future();
|
||||
}
|
||||
} else {
|
||||
if (const auto result =
|
||||
mutual_recursion_.maybe_handle(std::forward<F>(fn))) {
|
||||
std::promise<Result> result_promise;
|
||||
result_promise.set_value(std::move(*result));
|
||||
|
||||
return result_promise.get_future();
|
||||
}
|
||||
}
|
||||
|
||||
return plugin.run_on_main_thread(std::forward<F>(fn));
|
||||
}
|
||||
|
||||
/**
|
||||
* The logging facility used for this instance of yabridge. Wraps around
|
||||
@@ -230,12 +266,11 @@ class ClapPluginBridge : PluginBridge<ClapSockets<std::jthread>> {
|
||||
*/
|
||||
std::shared_mutex plugin_proxies_mutex_;
|
||||
|
||||
// TODO: Do we need this in CLAP?
|
||||
// /**
|
||||
// * Used in `ClapBridge::send_mutually_recursive_message()` to be able to
|
||||
// * execute functions from that same calling thread while we're waiting
|
||||
// for a
|
||||
// * response. This is used in `ClapPlugViewProxyImpl::run_loop_tasks()`.
|
||||
// */
|
||||
// MutualRecursionHelper<std::jthread> mutual_recursion_;
|
||||
/**
|
||||
* Used in `ClapBridge::send_mutually_recursive_message()` to be able to
|
||||
* execute functions from that same calling thread while we're waiting for a
|
||||
* response. See the uses for `send_mutually_recursive_message()` for use
|
||||
* cases where this is needed.
|
||||
*/
|
||||
MutualRecursionHelper<std::jthread> mutual_recursion_;
|
||||
};
|
||||
|
||||
@@ -143,7 +143,7 @@ class Vst3PlugViewProxyImpl : public Vst3PlugViewProxy {
|
||||
|
||||
// If `Vst3Bridge::send_mutually_recursive_message()` is currently being
|
||||
// called (because the host is calling one of `IPlugView`'s methods from
|
||||
// its UGI thread), then we'll call `fn` from that same thread.
|
||||
// its GUI thread), then we'll call `fn` from that same thread.
|
||||
// Otherwise we'll schedule the task to be run from an event handler
|
||||
// registered to the host's run loop, if that exists. Finally if the
|
||||
// host does not support `IRunLoop`, then we'll just run `fn` directly.
|
||||
|
||||
Reference in New Issue
Block a user