Implement the Wine side of clap_plugin::process()

This commit is contained in:
Robbert van der Helm
2022-10-03 01:17:47 +02:00
parent 984952fc4a
commit 80b224fcbd
2 changed files with 126 additions and 89 deletions
+35 -35
View File
@@ -110,48 +110,48 @@ struct ClapAudioThreadControlRequest {
template <typename T>
ClapAudioThreadControlRequest(T request) : payload(std::move(request)) {}
using Payload = std::variant<clap::plugin::StartProcessing,
clap::plugin::StopProcessing,
clap::plugin::Reset,
clap::ext::params::plugin::Flush,
clap::ext::tail::plugin::Get>;
using Payload =
std::variant<clap::plugin::StartProcessing,
clap::plugin::StopProcessing,
clap::plugin::Reset,
// The actual value for this will be stored in the
// `process_request` field on `clap_plugin_proxy`. That way
// we don't have to destroy the object (and deallocate all
// vectors in it) on the Wine side during every processing
// cycle.
MessageReference<clap::plugin::Process>,
clap::ext::params::plugin::Flush,
clap::ext::tail::plugin::Get>;
Payload payload;
template <typename S>
void serialize(S& s) {
s.ext(
payload,
bitsery::ext::InPlaceVariant{
// TODO: Process data
// [&](S& s,
// MessageReference<YaAudioProcessor::Process>& request_ref)
// {
// // When serializing this reference we'll read the data
// // directly from the referred to object. During
// // deserializing we'll deserialize into the persistent and
// // thread local `process_request` object (see
// // `ClapSockets::add_audio_processor_and_listen`) and then
// // reassign the reference to point to that object.
// s.ext(request_ref,
// bitsery::ext::MessageReference(process_request_));
// },
[](S& s, auto& request) { s.object(request); }});
s.ext(payload,
bitsery::ext::InPlaceVariant{
[&](S& s,
MessageReference<clap::plugin::Process>& request_ref) {
// When serializing this reference we'll read the data
// directly from the referred to object. During
// deserializing we'll deserialize into the persistent and
// thread local `process_request` object (see
// `ClapSockets::add_audio_thread_and_listen_control`) and
// then reassign the reference to point to that object.
s.ext(request_ref,
bitsery::ext::MessageReference(process_request_));
},
[](S& s, auto& request) { s.object(request); }});
}
// TODO: Process data, update docstring
// /**
// * Used for deserializing the
// `MessageReference<YaAudioProcessor::Process>`
// * variant. When we encounter this variant, we'll actually deserialize
// the
// * object into this object, and we'll then reassign the reference to
// point
// * to this object. That way we can keep it around as a thread local
// object
// * to prevent unnecessary allocations.
// */
// std::optional<YaAudioProcessor::Process> process_request_;
/**
* Used for deserializing the
* `MessageReference<clap::plugin::process::Process>` variant. When we
* encounter this variant, we'll actually deserialize the object into this
* object, and we'll then reassign the reference to point to this object.
* That way we can keep it around as a thread local object to prevent
* unnecessary allocations.
*/
std::optional<clap::plugin::Process> process_request_;
};
/**
+91 -54
View File
@@ -899,72 +899,109 @@ void ClapBridge::register_plugin_instance(
// Every plugin instance gets its own audio thread along with sockets for
// host->plugin control messages and plugin->host callbacks
std::promise<void> socket_listening_latch;
object_instances_.at(instance_id).audio_thread_handler =
Win32Thread([&, instance_id]() {
set_realtime_priority(true);
object_instances_.at(instance_id)
.audio_thread_handler = Win32Thread([&, instance_id]() {
set_realtime_priority(true);
// XXX: Like with VST2 worker threads, when using plugin groups the
// thread names from different plugins will clash. Not a huge
// deal probably, since duplicate thread names are still more
// useful than no thread names.
const std::string thread_name =
"audio-" + std::to_string(instance_id);
pthread_setname_np(pthread_self(), thread_name.c_str());
// XXX: Like with VST2 worker threads, when using plugin groups the
// thread names from different plugins will clash. Not a huge
// deal probably, since duplicate thread names are still more
// useful than no thread names.
const std::string thread_name = "audio-" + std::to_string(instance_id);
pthread_setname_np(pthread_self(), thread_name.c_str());
sockets_.add_audio_thread_and_listen_control(
instance_id, socket_listening_latch,
overload{
[&](const clap::plugin::StartProcessing& request)
-> clap::plugin::StartProcessing::Response {
const auto& [instance, _] =
get_instance(request.instance_id);
sockets_.add_audio_thread_and_listen_control(
instance_id, socket_listening_latch,
overload{
[&](const clap::plugin::StartProcessing& request)
-> clap::plugin::StartProcessing::Response {
const auto& [instance, _] =
get_instance(request.instance_id);
return instance.plugin->start_processing(
instance.plugin.get());
},
[&](const clap::plugin::StopProcessing& request)
-> clap::plugin::StopProcessing::Response {
const auto& [instance, _] =
get_instance(request.instance_id);
return instance.plugin->start_processing(
instance.plugin.get());
},
[&](const clap::plugin::StopProcessing& request)
-> clap::plugin::StopProcessing::Response {
const auto& [instance, _] =
get_instance(request.instance_id);
instance.plugin->stop_processing(instance.plugin.get());
instance.plugin->stop_processing(instance.plugin.get());
return Ack{};
},
[&](const clap::plugin::Reset& request)
-> clap::plugin::Reset::Response {
const auto& [instance, _] =
get_instance(request.instance_id);
return Ack{};
},
[&](const clap::plugin::Reset& request)
-> clap::plugin::Reset::Response {
const auto& [instance, _] =
get_instance(request.instance_id);
instance.plugin->reset(instance.plugin.get());
instance.plugin->reset(instance.plugin.get());
return Ack{};
},
return Ack{};
},
[&](const MessageReference<clap::plugin::Process>& request_ref)
-> clap::plugin::Process::Response {
// NOTE: To prevent allocations we keep this actual
// `clap::plugin::Process` object around as part of a
// static thread local `ClapAudioThreadControlRequest`
// object, and we only store a reference to it in our
// variant (this is done during the deserialization in
// `bitsery::ext::MessageReference`)
clap::plugin::Process& request = request_ref.get();
// As suggested by Jack Winter, we'll synchronize this
// thread's audio processing priority with that of the
// host's audio thread every once in a while
if (request.new_realtime_priority) {
set_realtime_priority(true,
*request.new_realtime_priority);
}
const auto& [instance, _] =
get_instance(request.instance_id);
// Most plugins will already enable FTZ, but there are a
// handful of plugins that don't that suffer from extreme
// DSP load increases when they start producing denormals
ScopedFlushToZero ftz_guard;
// The actual audio is stored in the shared memory
// buffers, so the reconstruction function will need to
// know where it should point the `AudioBusBuffers` to
// TODO: Once we add the render extension, process on the
// main thread when doing offline rendering
[&](clap::ext::params::plugin::Flush& request)
-> clap::ext::params::plugin::Flush::Response {
const auto& [instance, _] =
get_instance(request.instance_id);
auto& reconstructed = request.process.reconstruct(
instance.process_buffers_input_pointers,
instance.process_buffers_output_pointers);
clap_process_status result = instance.plugin->process(
instance.plugin.get(), &reconstructed);
clap::events::EventList out{};
instance.extensions.params->flush(
instance.plugin.get(), request.in.input_events(),
out.output_events());
return clap::plugin::ProcessResponse{
.result = result,
.output_data = request.process.create_response()};
},
[&](clap::ext::params::plugin::Flush& request)
-> clap::ext::params::plugin::Flush::Response {
const auto& [instance, _] =
get_instance(request.instance_id);
return clap::ext::params::plugin::FlushResponse{
.out = std::move(out)};
},
[&](const clap::ext::tail::plugin::Get& request)
-> clap::ext::tail::plugin::Get::Response {
const auto& [instance, _] =
get_instance(request.instance_id);
clap::events::EventList out{};
instance.extensions.params->flush(instance.plugin.get(),
request.in.input_events(),
out.output_events());
return instance.extensions.tail->get(
instance.plugin.get());
},
});
});
return clap::ext::params::plugin::FlushResponse{
.out = std::move(out)};
},
[&](const clap::ext::tail::plugin::Get& request)
-> clap::ext::tail::plugin::Get::Response {
const auto& [instance, _] =
get_instance(request.instance_id);
return instance.extensions.tail->get(instance.plugin.get());
},
});
});
// Wait for the new socket to be listening on before continuing. Otherwise
// the native plugin may try to connect to it before our thread is up and