From 51877796fa3f5f38fdf08f4c6e95c6e2b4f87b12 Mon Sep 17 00:00:00 2001 From: Robbert van der Helm Date: Mon, 21 Dec 2020 17:25:08 +0100 Subject: [PATCH] Add dedicated IAudioProcessor/IComponent sockets This way every relevant object instance will get its own thread for handling these calls. The alternative would be creating a full fat Vst3MessageHandler pair for all object instances, but that would be a huge waste. --- src/common/communication/common.h | 14 +- src/common/communication/vst3.h | 138 ++++- src/common/logging/vst3.cpp | 546 +++++++++--------- src/common/logging/vst3.h | 62 +- src/common/serialization/vst3.h | 44 +- .../bridges/vst3-impls/plugin-proxy.cpp | 76 +-- src/plugin/bridges/vst3.cpp | 25 +- src/plugin/bridges/vst3.h | 30 +- src/wine-host/bridges/vst3.cpp | 322 +++++++---- src/wine-host/bridges/vst3.h | 7 + 10 files changed, 764 insertions(+), 500 deletions(-) diff --git a/src/common/communication/common.h b/src/common/communication/common.h index 02008a8c..f3f97107 100644 --- a/src/common/communication/common.h +++ b/src/common/communication/common.h @@ -491,12 +491,14 @@ class AdHocSocketHandler { if (acceptor) { acceptor->accept(socket); - // As mentioned in `acceptor's` docstring, this acceptor will be - // recreated in `receive_multi()` on another context, and - // potentially on the other side of the connection in the case - // where we're handling `vst_host_callback` VST2 events - acceptor.reset(); - boost::filesystem::remove(endpoint.path()); + if constexpr (ad_hoc_sockets) { + // As mentioned in `acceptor's` docstring, this acceptor will be + // recreated in `receive_multi()` on another context, and + // potentially on the other side of the connection in the case + // where we're handling `vst_host_callback` VST2 events + acceptor.reset(); + boost::filesystem::remove(endpoint.path()); + } } else { socket.connect(endpoint); } diff --git a/src/common/communication/vst3.h b/src/common/communication/vst3.h index 84833317..9fb4b546 100644 --- a/src/common/communication/vst3.h +++ b/src/common/communication/vst3.h @@ -16,6 +16,7 @@ #pragma once +#include #include #include "../logging/vst3.h" @@ -221,6 +222,16 @@ class Vst3MessageHandler : public AdHocSocketHandler { * sockets, and the call to `connect()` will then accept any incoming * connections. * + * We'll have a host -> plugin connection for sending control messages (which is + * just a made up term to more easily differentiate between the two directions), + * and a plugin -> host connection to allow the plugin to make callbacks. Both + * of these connections are capable of spawning additional sockets and threads + * as needed. + * + * For audio processing (or anything that implement `IAudioProcessor` or + * `IComonent`) we'll use dedicated sockets per instance, since we don't want to + * do anything that could increase latency there. + * * @tparam Thread The thread implementation to use. On the Linux side this * should be `std::jthread` and on the Wine side this should be `Win32Thread`. */ @@ -250,7 +261,8 @@ class Vst3Sockets : public Sockets { listen), vst_host_callback(io_context, (base_dir / "vst_host_callback.sock").string(), - listen) {} + listen), + io_context(io_context) {} ~Vst3Sockets() { close(); } @@ -264,17 +276,113 @@ class Vst3Sockets : public Sockets { // that may still be active host_vst_control.close(); vst_host_callback.close(); + + // This map should be empty at this point, but who knows + std::lock_guard lock(audio_processor_sockets_mutex); + for (auto& [instance_id, socket] : audio_processor_sockets) { + socket.close(); + } } - // TODO: Since audio processing may be done completely in parallel we might - // want to have a dedicated socket per processor/controller pair. For - // this we would need to figure out how to associate a plugin instance - // with a socket. + /** + * Connect to the dedicated `IAudioProcessor` and `IConnect` handling socket + * for a plugin object instance. This should be called on the plugin side + * after instantiating such an object. + * + * @param instance_id The object instance identifier of the socket. + */ + void add_audio_processor_and_connect(size_t instance_id) { + std::lock_guard lock(audio_processor_sockets_mutex); + audio_processor_sockets.try_emplace( + instance_id, io_context, + (base_dir / ("host_vst_audio_processor_" + + std::to_string(instance_id) + ".sock")) + .string(), + false); + audio_processor_sockets.at(instance_id).connect(); + } + + /** + * Create and listen on a dedicated `IAudioProcessor` and `IConnect` + * handling socket for a plugin object instance. The calling thread will + * block until the socket has been closed. This should be called from the + * Wine plugin host side after instantiating such an object. + * + * @param instance_id The object instance identifier of the socket. + * @param socket_listening_latch A promise we'll set a value for once the + * socket is being listened on so we can wait for it. Otherwise it can be + * that the native plugin already tries to connect to the socket before + * Wine plugin host is even listening on it. + * @param cb An overloaded function that can take every type `T` in the + * `AudioProcessorRequest` variant and then returns `T::Response`. + */ + template + void add_audio_processor_and_listen( + size_t instance_id, + std::promise& socket_listening_latch, + F cb) { + { + std::lock_guard lock(audio_processor_sockets_mutex); + audio_processor_sockets.try_emplace( + instance_id, io_context, + (base_dir / ("host_vst_audio_processor_" + + std::to_string(instance_id) + ".sock")) + .string(), + true); + } + + socket_listening_latch.set_value(); + audio_processor_sockets.at(instance_id).connect(); + audio_processor_sockets.at(instance_id) + .receive_messages(std::nullopt, cb); + } + + /** + * If `instance_id` is in `audio_processor_sockets`, then close its socket + * and remove it from the map. This is called from the destructor of + * `Vst3PluginProxyImpl` on the plugin side and when handling + * `Vst3PluginProxy::Destruct` on the Wine plugin host side. + * + * @param instance_id The object instance identifier of the socket. + * + * @return Whether the socket was closed and removed. Returns false if it + * wasn't in the map. + */ + bool remove_audio_processor(size_t instance_id) { + std::lock_guard lock(audio_processor_sockets_mutex); + if (audio_processor_sockets.contains(instance_id)) { + audio_processor_sockets.at(instance_id).close(); + audio_processor_sockets.erase(instance_id); + + return true; + } else { + return false; + } + } + + /** + * Send a message from the native plugin to the Wine plugin host to handle + * an `IAudioProcessor` or `IComponent` call. Since those functions are + * called from a hot loop we want every instance to have a dedicated socket + * and thread for handling those. + * + * @tparam T Some object in the `AudioProcessorRequest` variant. + */ + template + typename T::Response send_audio_processor_message( + const T& object, + std::optional> logging) { + // TODO: These calls should reuse buffers + return audio_processor_sockets.at(object.instance_id) + .send_message(object, logging); + } /** * For sending messages from the host to the plugin. After we have a better * idea of what our communication model looks like we'll probably want to - * provide an abstraction similar to `EventHandler`. + * provide an abstraction similar to `EventHandler`. For optimization + * reasons calls to `IAudioProcessor` or `IComponent` are handled using the + * dedicated sockets in `audio_processor_sockets`. * * This will be listened on by the Wine plugin host when it calls * `receive_multi()`. @@ -287,4 +395,22 @@ class Vst3Sockets : public Sockets { * want to provide an abstraction similar to `EventHandler`. */ Vst3MessageHandler vst_host_callback; + + private: + boost::asio::io_context& io_context; + + /** + * Every `IAudioProcessor` or `IComponent` instance (which likely implements + * both of those) will get a dedicated socket. These functions are always + * called in a hot loop, so there should not be any waiting or additional + * thread or socket creation happening there. + * + * THe last `false` template arguments means that we'll disable all ad-hoc + * socket and thread spawning behaviour. Otherwise every plugin instance + * would have one dedicated thread for handling function calls to these + * interfaces, and then another dedicated thread just idling around. + */ + std::map> + audio_processor_sockets; + std::mutex audio_processor_sockets_mutex; }; diff --git a/src/common/logging/vst3.cpp b/src/common/logging/vst3.cpp index d747b314..f5c6983e 100644 --- a/src/common/logging/vst3.cpp +++ b/src/common/logging/vst3.cpp @@ -90,210 +90,6 @@ bool Vst3Logger::log_request(bool is_host_vst, }); } -bool Vst3Logger::log_request( - bool is_host_vst, - const YaAudioProcessor::SetBusArrangements& request) { - return log_request_base(is_host_vst, [&](auto& message) { - message << request.instance_id - << ": IAudioProcessor::setBusArrangements(inputs = " - "[SpeakerArrangement; " - << request.inputs.size() << "], numIns = " << request.num_ins - << ", outputs = [SpeakerArrangement; " << request.outputs.size() - << "], numOuts = " << request.num_outs << ")"; - }); -} - -bool Vst3Logger::log_request( - bool is_host_vst, - const YaAudioProcessor::GetBusArrangement& request) { - return log_request_base(is_host_vst, [&](auto& message) { - message << request.instance_id - << ": IAudioProcessor::getBusArrangement(dir = " << request.dir - << ", index = " << request.index << ", &arr)"; - }); -} - -bool Vst3Logger::log_request( - bool is_host_vst, - const YaAudioProcessor::CanProcessSampleSize& request) { - return log_request_base( - is_host_vst, Logger::Verbosity::all_events, [&](auto& message) { - message - << request.instance_id - << ": IAudioProcessor::canProcessSampleSize(symbolicSampleSize " - "= " - << request.symbolic_sample_size << ")"; - }); -} - -bool Vst3Logger::log_request( - bool is_host_vst, - const YaAudioProcessor::GetLatencySamples& request) { - return log_request_base(is_host_vst, [&](auto& message) { - message << request.instance_id - << ": IAudioProcessor::getLatencySamples()"; - }); -} - -bool Vst3Logger::log_request(bool is_host_vst, - const YaAudioProcessor::SetupProcessing& request) { - return log_request_base(is_host_vst, [&](auto& message) { - message << request.instance_id - << ": IAudioProcessor::setupProcessing(setup = " - ")"; - }); -} - -bool Vst3Logger::log_request(bool is_host_vst, - const YaAudioProcessor::SetProcessing& request) { - return log_request_base(is_host_vst, [&](auto& message) { - message << request.instance_id - << ": IAudioProcessor::setProcessing(state = " - << (request.state ? "true" : "false") << ")"; - }); -} - -bool Vst3Logger::log_request(bool is_host_vst, - const YaAudioProcessor::Process& request) { - return log_request_base( - is_host_vst, Logger::Verbosity::all_events, [&](auto& message) { - // This is incredibly verbose, but if you're really a plugin that - // handles processing in a weird way you're going to need all of - // this - - std::ostringstream num_input_channels; - num_input_channels << "["; - for (bool is_first = true; - const auto& buffers : request.data.inputs) { - num_input_channels << (is_first ? "" : ", ") - << buffers.num_channels(); - is_first = false; - } - num_input_channels << "]"; - - std::ostringstream num_output_channels; - num_output_channels << "["; - for (bool is_first = true; - const auto& num_channels : request.data.outputs_num_channels) { - num_output_channels << (is_first ? "" : ", ") << num_channels; - is_first = false; - } - num_output_channels << "]"; - - message << request.instance_id - << ": IAudioProcessor::process(data = , output_parameter_changes = " - << (request.data.output_parameter_changes_supported - ? "" - : "nullptr") - << ", input_events = "; - if (request.data.input_events) { - message << "num_events() - << " events>"; - } else { - message << "nullptr"; - } - message << ", output_events = " - << (request.data.output_events_supported ? "" - : "nullptr") - << ", process_context = " - << (request.data.process_context ? "" - : "nullptr") - << ", process_mode = " << request.data.process_mode - << ", symbolic_sample_size = " - << request.data.symbolic_sample_size << ">)"; - }); -} - -bool Vst3Logger::log_request(bool is_host_vst, - const YaAudioProcessor::GetTailSamples& request) { - return log_request_base( - is_host_vst, Logger::Verbosity::all_events, [&](auto& message) { - message << request.instance_id - << ": IAudioProcessor::getTailSamples()"; - }); -} - -bool Vst3Logger::log_request(bool is_host_vst, - const YaComponent::GetControllerClassId& request) { - return log_request_base(is_host_vst, [&](auto& message) { - message << request.instance_id - << ": IComponent::getControllerClassId(&classId)"; - }); -} - -bool Vst3Logger::log_request(bool is_host_vst, - const YaComponent::SetIoMode& request) { - return log_request_base(is_host_vst, [&](auto& message) { - message << request.instance_id - << ": IComponent::setIoMode(mode = " << request.mode << ")"; - }); -} - -bool Vst3Logger::log_request(bool is_host_vst, - const YaComponent::GetBusCount& request) { - // JUCE-based hosts will call this every processing cycle, for some reason - // (it shouldn't be allwoed to change during processing, right?) - return log_request_base( - is_host_vst, Logger::Verbosity::all_events, [&](auto& message) { - message << request.instance_id - << ": IComponent::getBusCount(type = " << request.type - << ", dir = " << request.dir << ")"; - }); -} - -bool Vst3Logger::log_request(bool is_host_vst, - const YaComponent::GetBusInfo& request) { - return log_request_base(is_host_vst, [&](auto& message) { - message << request.instance_id - << ": IComponent::getBusInfo(type = " << request.type - << ", dir = " << request.dir << ", index = " << request.index - << ", &bus)"; - }); -} - -bool Vst3Logger::log_request(bool is_host_vst, - const YaComponent::GetRoutingInfo& request) { - return log_request_base(is_host_vst, [&](auto& message) { - message - << request.instance_id - << ": IComponent::getRoutingInfo(inInfo = , outInfo = )"; - }); -} - -bool Vst3Logger::log_request(bool is_host_vst, - const YaComponent::ActivateBus& request) { - return log_request_base(is_host_vst, [&](auto& message) { - message << request.instance_id - << ": IComponent::activateBus(type = " << request.type - << ", dir = " << request.dir << ", index = " << request.index - << ", state = " << (request.state ? "true" : "false") << ")"; - }); -} - -bool Vst3Logger::log_request(bool is_host_vst, - const YaComponent::SetActive& request) { - return log_request_base(is_host_vst, [&](auto& message) { - message << request.instance_id << ": IComponent::setActive(state = " - << (request.state ? "true" : "false") << ")"; - }); -} - bool Vst3Logger::log_request(bool is_host_vst, const YaConnectionPoint::Connect& request) { return log_request_base(is_host_vst, [&](auto& message) { @@ -508,6 +304,210 @@ bool Vst3Logger::log_request(bool is_host_vst, }); } +bool Vst3Logger::log_request( + bool is_host_vst, + const YaAudioProcessor::SetBusArrangements& request) { + return log_request_base(is_host_vst, [&](auto& message) { + message << request.instance_id + << ": IAudioProcessor::setBusArrangements(inputs = " + "[SpeakerArrangement; " + << request.inputs.size() << "], numIns = " << request.num_ins + << ", outputs = [SpeakerArrangement; " << request.outputs.size() + << "], numOuts = " << request.num_outs << ")"; + }); +} + +bool Vst3Logger::log_request( + bool is_host_vst, + const YaAudioProcessor::GetBusArrangement& request) { + return log_request_base(is_host_vst, [&](auto& message) { + message << request.instance_id + << ": IAudioProcessor::getBusArrangement(dir = " << request.dir + << ", index = " << request.index << ", &arr)"; + }); +} + +bool Vst3Logger::log_request( + bool is_host_vst, + const YaAudioProcessor::CanProcessSampleSize& request) { + return log_request_base( + is_host_vst, Logger::Verbosity::all_events, [&](auto& message) { + message + << request.instance_id + << ": IAudioProcessor::canProcessSampleSize(symbolicSampleSize " + "= " + << request.symbolic_sample_size << ")"; + }); +} + +bool Vst3Logger::log_request( + bool is_host_vst, + const YaAudioProcessor::GetLatencySamples& request) { + return log_request_base(is_host_vst, [&](auto& message) { + message << request.instance_id + << ": IAudioProcessor::getLatencySamples()"; + }); +} + +bool Vst3Logger::log_request(bool is_host_vst, + const YaAudioProcessor::SetupProcessing& request) { + return log_request_base(is_host_vst, [&](auto& message) { + message << request.instance_id + << ": IAudioProcessor::setupProcessing(setup = " + ")"; + }); +} + +bool Vst3Logger::log_request(bool is_host_vst, + const YaAudioProcessor::SetProcessing& request) { + return log_request_base(is_host_vst, [&](auto& message) { + message << request.instance_id + << ": IAudioProcessor::setProcessing(state = " + << (request.state ? "true" : "false") << ")"; + }); +} + +bool Vst3Logger::log_request(bool is_host_vst, + const YaAudioProcessor::Process& request) { + return log_request_base( + is_host_vst, Logger::Verbosity::all_events, [&](auto& message) { + // This is incredibly verbose, but if you're really a plugin that + // handles processing in a weird way you're going to need all of + // this + + std::ostringstream num_input_channels; + num_input_channels << "["; + for (bool is_first = true; + const auto& buffers : request.data.inputs) { + num_input_channels << (is_first ? "" : ", ") + << buffers.num_channels(); + is_first = false; + } + num_input_channels << "]"; + + std::ostringstream num_output_channels; + num_output_channels << "["; + for (bool is_first = true; + const auto& num_channels : request.data.outputs_num_channels) { + num_output_channels << (is_first ? "" : ", ") << num_channels; + is_first = false; + } + num_output_channels << "]"; + + message << request.instance_id + << ": IAudioProcessor::process(data = , output_parameter_changes = " + << (request.data.output_parameter_changes_supported + ? "" + : "nullptr") + << ", input_events = "; + if (request.data.input_events) { + message << "num_events() + << " events>"; + } else { + message << "nullptr"; + } + message << ", output_events = " + << (request.data.output_events_supported ? "" + : "nullptr") + << ", process_context = " + << (request.data.process_context ? "" + : "nullptr") + << ", process_mode = " << request.data.process_mode + << ", symbolic_sample_size = " + << request.data.symbolic_sample_size << ">)"; + }); +} + +bool Vst3Logger::log_request(bool is_host_vst, + const YaAudioProcessor::GetTailSamples& request) { + return log_request_base( + is_host_vst, Logger::Verbosity::all_events, [&](auto& message) { + message << request.instance_id + << ": IAudioProcessor::getTailSamples()"; + }); +} + +bool Vst3Logger::log_request(bool is_host_vst, + const YaComponent::GetControllerClassId& request) { + return log_request_base(is_host_vst, [&](auto& message) { + message << request.instance_id + << ": IComponent::getControllerClassId(&classId)"; + }); +} + +bool Vst3Logger::log_request(bool is_host_vst, + const YaComponent::SetIoMode& request) { + return log_request_base(is_host_vst, [&](auto& message) { + message << request.instance_id + << ": IComponent::setIoMode(mode = " << request.mode << ")"; + }); +} + +bool Vst3Logger::log_request(bool is_host_vst, + const YaComponent::GetBusCount& request) { + // JUCE-based hosts will call this every processing cycle, for some reason + // (it shouldn't be allwoed to change during processing, right?) + return log_request_base( + is_host_vst, Logger::Verbosity::all_events, [&](auto& message) { + message << request.instance_id + << ": IComponent::getBusCount(type = " << request.type + << ", dir = " << request.dir << ")"; + }); +} + +bool Vst3Logger::log_request(bool is_host_vst, + const YaComponent::GetBusInfo& request) { + return log_request_base(is_host_vst, [&](auto& message) { + message << request.instance_id + << ": IComponent::getBusInfo(type = " << request.type + << ", dir = " << request.dir << ", index = " << request.index + << ", &bus)"; + }); +} + +bool Vst3Logger::log_request(bool is_host_vst, + const YaComponent::GetRoutingInfo& request) { + return log_request_base(is_host_vst, [&](auto& message) { + message + << request.instance_id + << ": IComponent::getRoutingInfo(inInfo = , outInfo = )"; + }); +} + +bool Vst3Logger::log_request(bool is_host_vst, + const YaComponent::ActivateBus& request) { + return log_request_base(is_host_vst, [&](auto& message) { + message << request.instance_id + << ": IComponent::activateBus(type = " << request.type + << ", dir = " << request.dir << ", index = " << request.index + << ", state = " << (request.state ? "true" : "false") << ")"; + }); +} + +bool Vst3Logger::log_request(bool is_host_vst, + const YaComponent::SetActive& request) { + return log_request_base(is_host_vst, [&](auto& message) { + message << request.instance_id << ": IComponent::setActive(state = " + << (request.state ? "true" : "false") << ")"; + }); +} + bool Vst3Logger::log_request(bool is_host_vst, const WantsConfiguration&) { return log_request_base(is_host_vst, [&](auto& message) { message << "Requesting "; @@ -593,6 +593,75 @@ void Vst3Logger::log_response( }); } +void Vst3Logger::log_response( + bool is_host_vst, + const YaEditController::GetParameterInfoResponse& response) { + log_response_base(is_host_vst, [&](auto& message) { + message << response.result.string(); + if (response.result == Steinberg::kResultOk) { + std::string param_title = + VST3::StringConvert::convert(response.updated_info.title); + message << ", "; + } + }); +} + +void Vst3Logger::log_response( + bool is_host_vst, + const YaEditController::GetParamStringByValueResponse& response) { + log_response_base(is_host_vst, [&](auto& message) { + message << response.result.string(); + if (response.result == Steinberg::kResultOk) { + std::string value = VST3::StringConvert::convert(response.string); + message << ", \"" << value << "\""; + } + }); +} + +void Vst3Logger::log_response( + bool is_host_vst, + const YaEditController::GetParamValueByStringResponse& response) { + log_response_base(is_host_vst, [&](auto& message) { + message << response.result.string(); + if (response.result == Steinberg::kResultOk) { + message << ", " << response.value_normalized; + } + }); +} + +void Vst3Logger::log_response( + bool is_host_vst, + const YaEditController::CreateViewResponse& response) { + log_response_base(is_host_vst, [&](auto& message) { + if (response.plug_view_args) { + message << ""; + } else { + message << ""; + } + }); +} + +void Vst3Logger::log_response(bool is_host_vst, + const YaPlugView::GetSizeResponse& response) { + log_response_base(is_host_vst, [&](auto& message) { + message << response.result.string(); + if (response.result == Steinberg::kResultOk) { + message << ", "; + } + }); +} + +void Vst3Logger::log_response(bool is_host_vst, + const YaPluginFactory::ConstructArgs& args) { + log_response_base(is_host_vst, [&](auto& message) { + message << " with " << args.num_classes + << " registered classes"; + }); +} + void Vst3Logger::log_response( bool is_host_vst, const YaAudioProcessor::GetBusArrangementResponse& response) { @@ -684,75 +753,6 @@ void Vst3Logger::log_response( }); } -void Vst3Logger::log_response( - bool is_host_vst, - const YaEditController::GetParameterInfoResponse& response) { - log_response_base(is_host_vst, [&](auto& message) { - message << response.result.string(); - if (response.result == Steinberg::kResultOk) { - std::string param_title = - VST3::StringConvert::convert(response.updated_info.title); - message << ", "; - } - }); -} - -void Vst3Logger::log_response( - bool is_host_vst, - const YaEditController::GetParamStringByValueResponse& response) { - log_response_base(is_host_vst, [&](auto& message) { - message << response.result.string(); - if (response.result == Steinberg::kResultOk) { - std::string value = VST3::StringConvert::convert(response.string); - message << ", \"" << value << "\""; - } - }); -} - -void Vst3Logger::log_response( - bool is_host_vst, - const YaEditController::GetParamValueByStringResponse& response) { - log_response_base(is_host_vst, [&](auto& message) { - message << response.result.string(); - if (response.result == Steinberg::kResultOk) { - message << ", " << response.value_normalized; - } - }); -} - -void Vst3Logger::log_response( - bool is_host_vst, - const YaEditController::CreateViewResponse& response) { - log_response_base(is_host_vst, [&](auto& message) { - if (response.plug_view_args) { - message << ""; - } else { - message << ""; - } - }); -} - -void Vst3Logger::log_response(bool is_host_vst, - const YaPlugView::GetSizeResponse& response) { - log_response_base(is_host_vst, [&](auto& message) { - message << response.result.string(); - if (response.result == Steinberg::kResultOk) { - message << ", "; - } - }); -} - -void Vst3Logger::log_response(bool is_host_vst, - const YaPluginFactory::ConstructArgs& args) { - log_response_base(is_host_vst, [&](auto& message) { - message << " with " << args.num_classes - << " registered classes"; - }); -} - void Vst3Logger::log_response(bool is_host_vst, const Configuration&) { log_response_base(is_host_vst, [&](auto& message) { message << ""; }); diff --git a/src/common/logging/vst3.h b/src/common/logging/vst3.h index 689375f3..d5c237e6 100644 --- a/src/common/logging/vst3.h +++ b/src/common/logging/vst3.h @@ -64,27 +64,6 @@ class Vst3Logger { bool log_request(bool is_host_vst, const Vst3PluginProxy::Destruct&); bool log_request(bool is_host_vst, const Vst3PluginProxy::SetState&); bool log_request(bool is_host_vst, const Vst3PluginProxy::GetState&); - bool log_request(bool is_host_vst, - const YaAudioProcessor::SetBusArrangements&); - bool log_request(bool is_host_vst, - const YaAudioProcessor::GetBusArrangement&); - bool log_request(bool is_host_vst, - const YaAudioProcessor::CanProcessSampleSize&); - bool log_request(bool is_host_vst, - const YaAudioProcessor::GetLatencySamples&); - bool log_request(bool is_host_vst, - const YaAudioProcessor::SetupProcessing&); - bool log_request(bool is_host_vst, const YaAudioProcessor::SetProcessing&); - bool log_request(bool is_host_vst, const YaAudioProcessor::Process&); - bool log_request(bool is_host_vst, const YaAudioProcessor::GetTailSamples&); - bool log_request(bool is_host_vst, - const YaComponent::GetControllerClassId&); - bool log_request(bool is_host_vst, const YaComponent::SetIoMode&); - bool log_request(bool is_host_vst, const YaComponent::GetBusCount&); - bool log_request(bool is_host_vst, const YaComponent::GetBusInfo&); - bool log_request(bool is_host_vst, const YaComponent::GetRoutingInfo&); - bool log_request(bool is_host_vst, const YaComponent::ActivateBus&); - bool log_request(bool is_host_vst, const YaComponent::SetActive&); bool log_request(bool is_host_vst, const YaConnectionPoint::Connect&); bool log_request(bool is_host_vst, const YaConnectionPoint::Disconnect&); bool log_request(bool is_host_vst, @@ -117,6 +96,28 @@ class Vst3Logger { bool log_request(bool is_host_vst, const YaPluginFactory::Construct&); bool log_request(bool is_host_vst, const YaPluginFactory::SetHostContext&); + bool log_request(bool is_host_vst, + const YaAudioProcessor::SetBusArrangements&); + bool log_request(bool is_host_vst, + const YaAudioProcessor::GetBusArrangement&); + bool log_request(bool is_host_vst, + const YaAudioProcessor::CanProcessSampleSize&); + bool log_request(bool is_host_vst, + const YaAudioProcessor::GetLatencySamples&); + bool log_request(bool is_host_vst, + const YaAudioProcessor::SetupProcessing&); + bool log_request(bool is_host_vst, const YaAudioProcessor::SetProcessing&); + bool log_request(bool is_host_vst, const YaAudioProcessor::Process&); + bool log_request(bool is_host_vst, const YaAudioProcessor::GetTailSamples&); + bool log_request(bool is_host_vst, + const YaComponent::GetControllerClassId&); + bool log_request(bool is_host_vst, const YaComponent::SetIoMode&); + bool log_request(bool is_host_vst, const YaComponent::GetBusCount&); + bool log_request(bool is_host_vst, const YaComponent::GetBusInfo&); + bool log_request(bool is_host_vst, const YaComponent::GetRoutingInfo&); + bool log_request(bool is_host_vst, const YaComponent::ActivateBus&); + bool log_request(bool is_host_vst, const YaComponent::SetActive&); + bool log_request(bool is_host_vst, const WantsConfiguration&); bool log_request(bool is_host_vst, const YaComponentHandler::BeginEdit&); bool log_request(bool is_host_vst, const YaComponentHandler::PerformEdit&); @@ -131,15 +132,6 @@ class Vst3Logger { const std::variant&); void log_response(bool is_host_vst, const Vst3PluginProxy::GetStateResponse&); - void log_response(bool is_host_vst, - const YaAudioProcessor::GetBusArrangementResponse&); - void log_response(bool is_host_vst, - const YaAudioProcessor::ProcessResponse&); - void log_response(bool is_host_vst, - const YaComponent::GetControllerClassIdResponse&); - void log_response(bool is_host_vst, const YaComponent::GetBusInfoResponse&); - void log_response(bool is_host_vst, - const YaComponent::GetRoutingInfoResponse&); void log_response(bool is_host_vst, const YaEditController::GetParameterInfoResponse&); void log_response(bool is_host_vst, @@ -152,6 +144,16 @@ class Vst3Logger { void log_response(bool is_host_vst, const YaPluginFactory::ConstructArgs&); void log_response(bool is_host_vst, const Configuration&); + void log_response(bool is_host_vst, + const YaAudioProcessor::GetBusArrangementResponse&); + void log_response(bool is_host_vst, + const YaAudioProcessor::ProcessResponse&); + void log_response(bool is_host_vst, + const YaComponent::GetControllerClassIdResponse&); + void log_response(bool is_host_vst, const YaComponent::GetBusInfoResponse&); + void log_response(bool is_host_vst, + const YaComponent::GetRoutingInfoResponse&); + void log_response(bool is_host_vst, const YaHostApplication::GetNameResponse&); diff --git a/src/common/serialization/vst3.h b/src/common/serialization/vst3.h index 8b4d4f05..d41c52f3 100644 --- a/src/common/serialization/vst3.h +++ b/src/common/serialization/vst3.h @@ -64,21 +64,6 @@ using ControlRequest = std::variant; + +template +void serialize(S& s, AudioProcessorRequest& payload) { + // All of the objects in `AudioProcessorRequest` should have their own + // serialization function. + s.ext(payload, bitsery::ext::StdVariant{}); +} + /** * When we do a callback from the Wine VST host to the plugin, this encodes the * information we want or the operation we want to perform. A request of type diff --git a/src/plugin/bridges/vst3-impls/plugin-proxy.cpp b/src/plugin/bridges/vst3-impls/plugin-proxy.cpp index 6f4830b8..91056187 100644 --- a/src/plugin/bridges/vst3-impls/plugin-proxy.cpp +++ b/src/plugin/bridges/vst3-impls/plugin-proxy.cpp @@ -27,7 +27,7 @@ Vst3PluginProxyImpl::Vst3PluginProxyImpl(Vst3PluginBridge& bridge, Vst3PluginProxyImpl::~Vst3PluginProxyImpl() { bridge.send_message( Vst3PluginProxy::Destruct{.instance_id = instance_id()}); - bridge.unregister_plugin_proxy(instance_id()); + bridge.unregister_plugin_proxy(*this); } tresult PLUGIN_API @@ -49,29 +49,32 @@ tresult PLUGIN_API Vst3PluginProxyImpl::setBusArrangements( int32 numOuts) { // NOTE: Ardour passes a null pointer when `numIns` or `numOuts` is 0, so we // need to work around that - return bridge.send_message(YaAudioProcessor::SetBusArrangements{ - .instance_id = instance_id(), - .inputs = (inputs ? std::vector( - inputs, &inputs[numIns]) - : std::vector()), - .num_ins = numIns, - .outputs = - (outputs ? std::vector( - outputs, &outputs[numOuts]) - : std::vector()), - .num_outs = numOuts, - }); + return bridge.send_audio_processor_message( + YaAudioProcessor::SetBusArrangements{ + .instance_id = instance_id(), + .inputs = + (inputs ? std::vector( + inputs, &inputs[numIns]) + : std::vector()), + .num_ins = numIns, + .outputs = + (outputs ? std::vector( + outputs, &outputs[numOuts]) + : std::vector()), + .num_outs = numOuts, + }); } tresult PLUGIN_API Vst3PluginProxyImpl::getBusArrangement( Steinberg::Vst::BusDirection dir, int32 index, Steinberg::Vst::SpeakerArrangement& arr) { - const GetBusArrangementResponse response = bridge.send_message( - YaAudioProcessor::GetBusArrangement{.instance_id = instance_id(), - .dir = dir, - .index = index, - .arr = arr}); + const GetBusArrangementResponse response = + bridge.send_audio_processor_message( + YaAudioProcessor::GetBusArrangement{.instance_id = instance_id(), + .dir = dir, + .index = index, + .arr = arr}); arr = response.updated_arr; @@ -80,24 +83,26 @@ tresult PLUGIN_API Vst3PluginProxyImpl::getBusArrangement( tresult PLUGIN_API Vst3PluginProxyImpl::canProcessSampleSize(int32 symbolicSampleSize) { - return bridge.send_message(YaAudioProcessor::CanProcessSampleSize{ - .instance_id = instance_id(), - .symbolic_sample_size = symbolicSampleSize}); + return bridge.send_audio_processor_message( + YaAudioProcessor::CanProcessSampleSize{ + .instance_id = instance_id(), + .symbolic_sample_size = symbolicSampleSize}); } uint32 PLUGIN_API Vst3PluginProxyImpl::getLatencySamples() { - return bridge.send_message( + return bridge.send_audio_processor_message( YaAudioProcessor::GetLatencySamples{.instance_id = instance_id()}); } tresult PLUGIN_API Vst3PluginProxyImpl::setupProcessing(Steinberg::Vst::ProcessSetup& setup) { - return bridge.send_message(YaAudioProcessor::SetupProcessing{ - .instance_id = instance_id(), .setup = setup}); + return bridge.send_audio_processor_message( + YaAudioProcessor::SetupProcessing{.instance_id = instance_id(), + .setup = setup}); } tresult PLUGIN_API Vst3PluginProxyImpl::setProcessing(TBool state) { - return bridge.send_message(YaAudioProcessor::SetProcessing{ + return bridge.send_audio_processor_message(YaAudioProcessor::SetProcessing{ .instance_id = instance_id(), .state = state}); } @@ -105,7 +110,7 @@ tresult PLUGIN_API Vst3PluginProxyImpl::process(Steinberg::Vst::ProcessData& data) { // TODO: Check whether reusing a `YaProcessData` object make a difference in // terms of performance - ProcessResponse response = bridge.send_message( + ProcessResponse response = bridge.send_audio_processor_message( YaAudioProcessor::Process{.instance_id = instance_id(), .data = data}); response.output_data.write_back_outputs(data); @@ -114,14 +119,15 @@ Vst3PluginProxyImpl::process(Steinberg::Vst::ProcessData& data) { } uint32 PLUGIN_API Vst3PluginProxyImpl::getTailSamples() { - return bridge.send_message( + return bridge.send_audio_processor_message( YaAudioProcessor::GetTailSamples{.instance_id = instance_id()}); } tresult PLUGIN_API Vst3PluginProxyImpl::getControllerClassId(Steinberg::TUID classId) { - const GetControllerClassIdResponse response = bridge.send_message( - YaComponent::GetControllerClassId{.instance_id = instance_id()}); + const GetControllerClassIdResponse response = + bridge.send_audio_processor_message( + YaComponent::GetControllerClassId{.instance_id = instance_id()}); std::copy(response.editor_cid.begin(), response.editor_cid.end(), classId); @@ -129,14 +135,14 @@ Vst3PluginProxyImpl::getControllerClassId(Steinberg::TUID classId) { } tresult PLUGIN_API Vst3PluginProxyImpl::setIoMode(Steinberg::Vst::IoMode mode) { - return bridge.send_message( + return bridge.send_audio_processor_message( YaComponent::SetIoMode{.instance_id = instance_id(), .mode = mode}); } int32 PLUGIN_API Vst3PluginProxyImpl::getBusCount(Steinberg::Vst::MediaType type, Steinberg::Vst::BusDirection dir) { - return bridge.send_message(YaComponent::GetBusCount{ + return bridge.send_audio_processor_message(YaComponent::GetBusCount{ .instance_id = instance_id(), .type = type, .dir = dir}); } @@ -145,7 +151,7 @@ Vst3PluginProxyImpl::getBusInfo(Steinberg::Vst::MediaType type, Steinberg::Vst::BusDirection dir, int32 index, Steinberg::Vst::BusInfo& bus /*out*/) { - const GetBusInfoResponse response = bridge.send_message( + const GetBusInfoResponse response = bridge.send_audio_processor_message( YaComponent::GetBusInfo{.instance_id = instance_id(), .type = type, .dir = dir, @@ -159,7 +165,7 @@ Vst3PluginProxyImpl::getBusInfo(Steinberg::Vst::MediaType type, tresult PLUGIN_API Vst3PluginProxyImpl::getRoutingInfo( Steinberg::Vst::RoutingInfo& inInfo, Steinberg::Vst::RoutingInfo& outInfo /*out*/) { - const GetRoutingInfoResponse response = bridge.send_message( + const GetRoutingInfoResponse response = bridge.send_audio_processor_message( YaComponent::GetRoutingInfo{.instance_id = instance_id(), .in_info = inInfo, .out_info = outInfo}); @@ -174,7 +180,7 @@ Vst3PluginProxyImpl::activateBus(Steinberg::Vst::MediaType type, Steinberg::Vst::BusDirection dir, int32 index, TBool state) { - return bridge.send_message( + return bridge.send_audio_processor_message( YaComponent::ActivateBus{.instance_id = instance_id(), .type = type, .dir = dir, @@ -183,7 +189,7 @@ Vst3PluginProxyImpl::activateBus(Steinberg::Vst::MediaType type, } tresult PLUGIN_API Vst3PluginProxyImpl::setActive(TBool state) { - return bridge.send_message( + return bridge.send_audio_processor_message( YaComponent::SetActive{.instance_id = instance_id(), .state = state}); } diff --git a/src/plugin/bridges/vst3.cpp b/src/plugin/bridges/vst3.cpp index 681d6bd3..e318fc44 100644 --- a/src/plugin/bridges/vst3.cpp +++ b/src/plugin/bridges/vst3.cpp @@ -161,13 +161,28 @@ Steinberg::IPluginFactory* Vst3PluginBridge::get_plugin_factory() { return plugin_factory; } -void Vst3PluginBridge::register_plugin_proxy(Vst3PluginProxyImpl& component) { +void Vst3PluginBridge::register_plugin_proxy( + Vst3PluginProxyImpl& proxy_object) { std::lock_guard lock(plugin_proxies_mutex); - plugin_proxies.emplace(component.instance_id(), - std::ref(component)); + + plugin_proxies.emplace(proxy_object.instance_id(), + std::ref(proxy_object)); + + // For optimization reaons we use dedicated sockets for functions that will + // be run in the audio processing loop + if (proxy_object.YaAudioProcessor::supported() || + proxy_object.YaComponent::supported()) { + sockets.add_audio_processor_and_connect(proxy_object.instance_id()); + } } -void Vst3PluginBridge::unregister_plugin_proxy(size_t instance_id) { +void Vst3PluginBridge::unregister_plugin_proxy( + Vst3PluginProxyImpl& proxy_object) { std::lock_guard lock(plugin_proxies_mutex); - plugin_proxies.erase(instance_id); + + plugin_proxies.erase(proxy_object.instance_id()); + if (proxy_object.YaAudioProcessor::supported() || + proxy_object.YaComponent::supported()) { + sockets.remove_audio_processor(proxy_object.instance_id()); + } } diff --git a/src/plugin/bridges/vst3.h b/src/plugin/bridges/vst3.h index 613b89f1..21deb51b 100644 --- a/src/plugin/bridges/vst3.h +++ b/src/plugin/bridges/vst3.h @@ -76,13 +76,12 @@ class Vst3PluginBridge : PluginBridge> { /** * Add a `Vst3PluginProxyImpl` to the list of registered proxy objects so we * can handle host callbacks. This function is called in - * `Vst3PluginProxyImpl`'s constructor. + * `Vst3PluginProxyImpl`'s constructor. If the plugin supports the + * `IAudioProcessor` or `IComponent` interfaces, then we'll also connect to + * a dedicated audio processing socket. * - * @param instance_id The instance ID generated by the plugin host when - * instantiating the `IComponent`. Used as a stable name to refer to - * these. - * @param plugin_proxy The actual proxy object we can access its host - * context. + * @param proxy_object The proxy object so we can access its host context + * and unique instance identifier. * * @see plugin_proxies */ @@ -93,13 +92,12 @@ class Vst3PluginBridge : PluginBridge> { * registered proxy objects. Called during the object's destructor after * asking the Wine plugin host to destroy the component on its side. * - * @param instance_id The instance ID generated by the plugin host when - * instantiating the `IComponent`. Used as a stable name to refer to - * these. + * @param proxy_object The proxy object so we can access its unique instance + * identifier. * * @see plugin_proxies */ - void unregister_plugin_proxy(size_t instance_id); + void unregister_plugin_proxy(Vst3PluginProxyImpl& proxy_object); /** * Send a control message to the Wine plugin host return the response. This @@ -112,6 +110,18 @@ class Vst3PluginBridge : PluginBridge> { object, std::pair(logger, true)); } + /** + * Send an `IAudioProcessor` or `IComponent` control message to a specific + * plugin instance. This is separated from the above `send_message()` for + * performance reasons, as this way every instance has its own dedicated + * socket and thread. + */ + template + typename T::Response send_audio_processor_message(const T& object) { + return sockets.send_audio_processor_message( + object, std::pair(logger, true)); + } + /** * The logging facility used for this instance of yabridge. Wraps around * `PluginBridge::generic_logger`. diff --git a/src/wine-host/bridges/vst3.cpp b/src/wine-host/bridges/vst3.cpp index 8f110fc1..cd95a5a9 100644 --- a/src/wine-host/bridges/vst3.cpp +++ b/src/wine-host/bridges/vst3.cpp @@ -116,28 +116,215 @@ void Vst3Bridge::run() { }) .get(); - if (object) { - std::lock_guard lock(object_instances_mutex); - - const size_t instance_id = generate_instance_id(); - object_instances.emplace(instance_id, std::move(object)); - - // This is where the magic happens. Here we deduce which - // interfaces are supported by this object so we can create - // a one-to-one proxy of it. - return Vst3PluginProxy::ConstructArgs( - object_instances[instance_id].object, instance_id); - } else { + if (!object) { return UniversalTResult(Steinberg::kResultFalse); } + + std::lock_guard lock(object_instances_mutex); + + const size_t instance_id = generate_instance_id(); + object_instances.emplace(instance_id, std::move(object)); + + // If the object supports `IComponent` or `IAudioProcessor`, + // then we'll set up a dedicated thread for function calls for + // those interfaces. + if (object_instances[instance_id].audio_processor || + object_instances[instance_id].component) { + std::promise socket_listening_latch; + object_instances[instance_id] + .audio_processor_handler = Win32Thread([&, + instance_id]() { + // TODO: Move this somewhere else, because this is + // obviously ridiculous + sockets.add_audio_processor_and_listen( + instance_id, socket_listening_latch, + overload{ + [&](YaAudioProcessor::SetBusArrangements& + request) + -> YaAudioProcessor::SetBusArrangements:: + Response { + return object_instances + [request.instance_id] + .audio_processor + ->setBusArrangements( + request.inputs.data(), + request.num_ins, + request.outputs.data(), + request.num_outs); + }, + [&](YaAudioProcessor::GetBusArrangement& + request) + -> YaAudioProcessor::GetBusArrangement:: + Response { + const tresult result = + object_instances + [request.instance_id] + .audio_processor + ->getBusArrangement( + request.dir, + request.index, + request.arr); + + return YaAudioProcessor:: + GetBusArrangementResponse{ + .result = result, + .updated_arr = request.arr}; + }, + [&](const YaAudioProcessor:: + CanProcessSampleSize& request) + -> YaAudioProcessor::CanProcessSampleSize:: + Response { + return object_instances + [request.instance_id] + .audio_processor + ->canProcessSampleSize( + request + .symbolic_sample_size); + }, + [&](const YaAudioProcessor::GetLatencySamples& + request) + -> YaAudioProcessor::GetLatencySamples:: + Response { + return object_instances + [request.instance_id] + .audio_processor + ->getLatencySamples(); + }, + [&](YaAudioProcessor::SetupProcessing& request) + -> YaAudioProcessor::SetupProcessing:: + Response { + return object_instances + [request.instance_id] + .audio_processor + ->setupProcessing( + request.setup); + }, + [&](const YaAudioProcessor::SetProcessing& + request) + -> YaAudioProcessor::SetProcessing:: + Response { + return object_instances + [request.instance_id] + .audio_processor + ->setProcessing( + request.state); + }, + [&](YaAudioProcessor::Process& request) + -> YaAudioProcessor::Process::Response { + const tresult result = + object_instances[request.instance_id] + .audio_processor->process( + request.data.get()); + + return YaAudioProcessor::ProcessResponse{ + .result = result, + .output_data = + request.data + .move_outputs_to_response()}; + }, + [&](const YaAudioProcessor::GetTailSamples& + request) + -> YaAudioProcessor::GetTailSamples:: + Response { + return object_instances + [request.instance_id] + .audio_processor + ->getTailSamples(); + }, + [&](const YaComponent::GetControllerClassId& + request) + -> YaComponent::GetControllerClassId:: + Response { + Steinberg::TUID cid; + const tresult result = + object_instances + [request.instance_id] + .component + ->getControllerClassId( + cid); + + return YaComponent:: + GetControllerClassIdResponse{ + .result = result, + .editor_cid = + std::to_array(cid)}; + }, + [&](const YaComponent::SetIoMode& request) + -> YaComponent::SetIoMode::Response { + return object_instances[request.instance_id] + .component->setIoMode(request.mode); + }, + [&](const YaComponent::GetBusCount& request) + -> YaComponent::GetBusCount::Response { + return object_instances[request.instance_id] + .component->getBusCount(request.type, + request.dir); + }, + [&](YaComponent::GetBusInfo& request) + -> YaComponent::GetBusInfo::Response { + const tresult result = + object_instances[request.instance_id] + .component->getBusInfo( + request.type, request.dir, + request.index, request.bus); + + return YaComponent::GetBusInfoResponse{ + .result = result, + .updated_bus = request.bus}; + }, + [&](YaComponent::GetRoutingInfo& request) + -> YaComponent::GetRoutingInfo::Response { + const tresult result = + object_instances[request.instance_id] + .component->getRoutingInfo( + request.in_info, + request.out_info); + + return YaComponent::GetRoutingInfoResponse{ + .result = result, + .updated_in_info = request.in_info, + .updated_out_info = request.out_info}; + }, + [&](const YaComponent::ActivateBus& request) + -> YaComponent::ActivateBus::Response { + return object_instances[request.instance_id] + .component->activateBus( + request.type, request.dir, + request.index, request.state); + }, + [&](const YaComponent::SetActive& request) + -> YaComponent::SetActive::Response { + return object_instances[request.instance_id] + .component->setActive(request.state); + }, + }); + }); + + // 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 running. + socket_listening_latch.get_future().wait(); + } + + // This is where the magic happens. Here we deduce which + // interfaces are supported by this object so we can create + // a one-to-one proxy of it. + return Vst3PluginProxy::ConstructArgs( + object_instances[instance_id].object, instance_id); }, [&](const Vst3PluginProxy::Destruct& request) -> Vst3PluginProxy::Destruct::Response { + // Tear the dedicated audio processing socket down again if we + // created one while handling `Vst3PluginProxy::Construct` + if (object_instances[request.instance_id].audio_processor || + object_instances[request.instance_id].component) { + sockets.remove_audio_processor(request.instance_id); + } + + // Remove the instance from within the main IO context so + // removing it doesn't interfere with the Win32 message loop main_context .run_in_context([&]() { - // Remove the instance from within the main IO context - // so removing it doesn't interfere with the Win32 - // message loop std::lock_guard lock(object_instances_mutex); object_instances.erase(request.instance_id); }) @@ -181,111 +368,6 @@ void Vst3Bridge::run() { return Vst3PluginProxy::GetStateResponse{ .result = result, .updated_state = std::move(stream)}; }, - [&](YaAudioProcessor::SetBusArrangements& request) - -> YaAudioProcessor::SetBusArrangements::Response { - return object_instances[request.instance_id] - .audio_processor->setBusArrangements( - request.inputs.data(), request.num_ins, - request.outputs.data(), request.num_outs); - }, - [&](YaAudioProcessor::GetBusArrangement& request) - -> YaAudioProcessor::GetBusArrangement::Response { - const tresult result = - object_instances[request.instance_id] - .audio_processor->getBusArrangement( - request.dir, request.index, request.arr); - - return YaAudioProcessor::GetBusArrangementResponse{ - .result = result, .updated_arr = request.arr}; - }, - [&](const YaAudioProcessor::CanProcessSampleSize& request) - -> YaAudioProcessor::CanProcessSampleSize::Response { - return object_instances[request.instance_id] - .audio_processor->canProcessSampleSize( - request.symbolic_sample_size); - }, - [&](const YaAudioProcessor::GetLatencySamples& request) - -> YaAudioProcessor::GetLatencySamples::Response { - return object_instances[request.instance_id] - .audio_processor->getLatencySamples(); - }, - [&](YaAudioProcessor::SetupProcessing& request) - -> YaAudioProcessor::SetupProcessing::Response { - return object_instances[request.instance_id] - .audio_processor->setupProcessing(request.setup); - }, - [&](const YaAudioProcessor::SetProcessing& request) - -> YaAudioProcessor::SetProcessing::Response { - return object_instances[request.instance_id] - .audio_processor->setProcessing(request.state); - }, - [&](YaAudioProcessor::Process& request) - -> YaAudioProcessor::Process::Response { - const tresult result = - object_instances[request.instance_id] - .audio_processor->process(request.data.get()); - - return YaAudioProcessor::ProcessResponse{ - .result = result, - .output_data = request.data.move_outputs_to_response()}; - }, - [&](const YaAudioProcessor::GetTailSamples& request) - -> YaAudioProcessor::GetTailSamples::Response { - return object_instances[request.instance_id] - .audio_processor->getTailSamples(); - }, - [&](const YaComponent::GetControllerClassId& request) - -> YaComponent::GetControllerClassId::Response { - Steinberg::TUID cid; - const tresult result = - object_instances[request.instance_id] - .component->getControllerClassId(cid); - - return YaComponent::GetControllerClassIdResponse{ - .result = result, .editor_cid = std::to_array(cid)}; - }, - [&](const YaComponent::SetIoMode& request) - -> YaComponent::SetIoMode::Response { - return object_instances[request.instance_id] - .component->setIoMode(request.mode); - }, - [&](const YaComponent::GetBusCount& request) - -> YaComponent::GetBusCount::Response { - return object_instances[request.instance_id] - .component->getBusCount(request.type, request.dir); - }, - [&](YaComponent::GetBusInfo& request) - -> YaComponent::GetBusInfo::Response { - const tresult result = - object_instances[request.instance_id].component->getBusInfo( - request.type, request.dir, request.index, request.bus); - - return YaComponent::GetBusInfoResponse{ - .result = result, .updated_bus = request.bus}; - }, - [&](YaComponent::GetRoutingInfo& request) - -> YaComponent::GetRoutingInfo::Response { - const tresult result = - object_instances[request.instance_id] - .component->getRoutingInfo(request.in_info, - request.out_info); - - return YaComponent::GetRoutingInfoResponse{ - .result = result, - .updated_in_info = request.in_info, - .updated_out_info = request.out_info}; - }, - [&](const YaComponent::ActivateBus& request) - -> YaComponent::ActivateBus::Response { - return object_instances[request.instance_id] - .component->activateBus(request.type, request.dir, - request.index, request.state); - }, - [&](const YaComponent::SetActive& request) - -> YaComponent::SetActive::Response { - return object_instances[request.instance_id] - .component->setActive(request.state); - }, [&](const YaConnectionPoint::Connect& request) -> YaConnectionPoint::Connect::Response { // We can directly connect the underlying objects diff --git a/src/wine-host/bridges/vst3.h b/src/wine-host/bridges/vst3.h index 026ceb53..525c2817 100644 --- a/src/wine-host/bridges/vst3.h +++ b/src/wine-host/bridges/vst3.h @@ -38,6 +38,13 @@ struct InstanceInterfaces { InstanceInterfaces(Steinberg::IPtr object); + /** + * A dedicated thread for handling incoming `IAudioProcessor` and + * `IComponent` calls. Will be instantiated if `object` supports either of + * those interfaces. + */ + Win32Thread audio_processor_handler; + /** * If the host passes a host context object during * `IPluginBase::initialize()`, we'll store a proxy object here and then