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.
This commit is contained in:
Robbert van der Helm
2020-12-21 17:25:08 +01:00
parent 415c1b5683
commit 51877796fa
10 changed files with 764 additions and 500 deletions
+8 -6
View File
@@ -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);
}
+132 -6
View File
@@ -16,6 +16,7 @@
#pragma once
#include <future>
#include <variant>
#include "../logging/vst3.h"
@@ -221,6 +222,16 @@ class Vst3MessageHandler : public AdHocSocketHandler<Thread, ad_hoc_sockets> {
* 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 <typename F>
void add_audio_processor_and_listen(
size_t instance_id,
std::promise<void>& 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>
typename T::Response send_audio_processor_message(
const T& object,
std::optional<std::pair<Vst3Logger&, bool>> 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<Thread, CallbackRequest, true> 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<size_t, Vst3MessageHandler<Thread, AudioProcessorRequest, false>>
audio_processor_sockets;
std::mutex audio_processor_sockets_mutex;
};
+273 -273
View File
@@ -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 = "
"<SetupProcessing with mode = "
<< request.setup.processMode << ", symbolic_sample_size = "
<< request.setup.symbolicSampleSize
<< ", max_buffer_size = " << request.setup.maxSamplesPerBlock
<< " and sample_rate = " << request.setup.sampleRate << ">)";
});
}
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 = <ProcessData with "
"input_channels = "
<< num_input_channels.str()
<< ", output_channels = " << num_output_channels.str()
<< ", num_samples = " << request.data.process_mode
<< ", input_parameter_changes = <IParameterChanges* for "
<< request.data.input_parameter_changes.num_parameters()
<< " parameters>, output_parameter_changes = "
<< (request.data.output_parameter_changes_supported
? "<IParameterChanges*>"
: "nullptr")
<< ", input_events = ";
if (request.data.input_events) {
message << "<IEventList* with "
<< request.data.input_events->num_events()
<< " events>";
} else {
message << "nullptr";
}
message << ", output_events = "
<< (request.data.output_events_supported ? "<IEventList*>"
: "nullptr")
<< ", process_context = "
<< (request.data.process_context ? "<ProcessContext*>"
: "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 = <RoutingInfo& for bus "
<< request.in_info.busIndex << " and channel "
<< request.in_info.channel << ">, outInfo = <RoutingInfo& for bus "
<< request.out_info.busIndex << " and channel "
<< request.out_info.channel << ">)";
});
}
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 = "
"<SetupProcessing with mode = "
<< request.setup.processMode << ", symbolic_sample_size = "
<< request.setup.symbolicSampleSize
<< ", max_buffer_size = " << request.setup.maxSamplesPerBlock
<< " and sample_rate = " << request.setup.sampleRate << ">)";
});
}
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 = <ProcessData with "
"input_channels = "
<< num_input_channels.str()
<< ", output_channels = " << num_output_channels.str()
<< ", num_samples = " << request.data.process_mode
<< ", input_parameter_changes = <IParameterChanges* for "
<< request.data.input_parameter_changes.num_parameters()
<< " parameters>, output_parameter_changes = "
<< (request.data.output_parameter_changes_supported
? "<IParameterChanges*>"
: "nullptr")
<< ", input_events = ";
if (request.data.input_events) {
message << "<IEventList* with "
<< request.data.input_events->num_events()
<< " events>";
} else {
message << "nullptr";
}
message << ", output_events = "
<< (request.data.output_events_supported ? "<IEventList*>"
: "nullptr")
<< ", process_context = "
<< (request.data.process_context ? "<ProcessContext*>"
: "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 = <RoutingInfo& for bus "
<< request.in_info.busIndex << " and channel "
<< request.in_info.channel << ">, outInfo = <RoutingInfo& for bus "
<< request.out_info.busIndex << " and channel "
<< request.out_info.channel << ">)";
});
}
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 <Configuration>";
@@ -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 << ", <ParameterInfo for '" << param_title << "'>";
}
});
}
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 << "<IPlugView*>";
} else {
message << "<nullptr>";
}
});
}
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 << ", <ViewRect* with left = " << response.updated_size.left
<< ", top = " << response.updated_size.top
<< ", right = " << response.updated_size.right
<< ", bottom = " << response.updated_size.bottom << ">";
}
});
}
void Vst3Logger::log_response(bool is_host_vst,
const YaPluginFactory::ConstructArgs& args) {
log_response_base(is_host_vst, [&](auto& message) {
message << "<IPluginFactory*> 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 << ", <ParameterInfo for '" << param_title << "'>";
}
});
}
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 << "<IPlugView*>";
} else {
message << "<nullptr>";
}
});
}
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 << ", <ViewRect* with left = " << response.updated_size.left
<< ", top = " << response.updated_size.top
<< ", right = " << response.updated_size.right
<< ", bottom = " << response.updated_size.bottom << ">";
}
});
}
void Vst3Logger::log_response(bool is_host_vst,
const YaPluginFactory::ConstructArgs& args) {
log_response_base(is_host_vst, [&](auto& message) {
message << "<IPluginFactory*> 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 << "<Configuration>"; });
+32 -30
View File
@@ -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<Vst3PluginProxy::ConstructArgs, UniversalTResult>&);
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&);
+29 -15
View File
@@ -64,21 +64,6 @@ using ControlRequest = std::variant<Vst3PlugViewProxy::Destruct,
Vst3PluginProxy::Destruct,
Vst3PluginProxy::SetState,
Vst3PluginProxy::GetState,
YaAudioProcessor::SetBusArrangements,
YaAudioProcessor::GetBusArrangement,
YaAudioProcessor::CanProcessSampleSize,
YaAudioProcessor::GetLatencySamples,
YaAudioProcessor::SetupProcessing,
YaAudioProcessor::SetProcessing,
YaAudioProcessor::Process,
YaAudioProcessor::GetTailSamples,
YaComponent::GetControllerClassId,
YaComponent::SetIoMode,
YaComponent::GetBusCount,
YaComponent::GetBusInfo,
YaComponent::GetRoutingInfo,
YaComponent::ActivateBus,
YaComponent::SetActive,
YaConnectionPoint::Connect,
YaConnectionPoint::Disconnect,
YaEditController::SetComponentState,
@@ -107,6 +92,35 @@ void serialize(S& s, ControlRequest& payload) {
s.ext(payload, bitsery::ext::StdVariant{});
}
/**
* A subset of all functions a host can call on a plugin. These functions are
* called from a hot loop every processing cycle, so we want a dedicated socket
* for these for every plugin instance.
*/
using AudioProcessorRequest =
std::variant<YaAudioProcessor::SetBusArrangements,
YaAudioProcessor::GetBusArrangement,
YaAudioProcessor::CanProcessSampleSize,
YaAudioProcessor::GetLatencySamples,
YaAudioProcessor::SetupProcessing,
YaAudioProcessor::SetProcessing,
YaAudioProcessor::Process,
YaAudioProcessor::GetTailSamples,
YaComponent::GetControllerClassId,
YaComponent::SetIoMode,
YaComponent::GetBusCount,
YaComponent::GetBusInfo,
YaComponent::GetRoutingInfo,
YaComponent::ActivateBus,
YaComponent::SetActive>;
template <typename S>
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
+41 -35
View File
@@ -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<Steinberg::Vst::SpeakerArrangement>(
inputs, &inputs[numIns])
: std::vector<Steinberg::Vst::SpeakerArrangement>()),
.num_ins = numIns,
.outputs =
(outputs ? std::vector<Steinberg::Vst::SpeakerArrangement>(
outputs, &outputs[numOuts])
: std::vector<Steinberg::Vst::SpeakerArrangement>()),
.num_outs = numOuts,
});
return bridge.send_audio_processor_message(
YaAudioProcessor::SetBusArrangements{
.instance_id = instance_id(),
.inputs =
(inputs ? std::vector<Steinberg::Vst::SpeakerArrangement>(
inputs, &inputs[numIns])
: std::vector<Steinberg::Vst::SpeakerArrangement>()),
.num_ins = numIns,
.outputs =
(outputs ? std::vector<Steinberg::Vst::SpeakerArrangement>(
outputs, &outputs[numOuts])
: std::vector<Steinberg::Vst::SpeakerArrangement>()),
.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});
}
+20 -5
View File
@@ -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<Vst3PluginProxyImpl>(component));
plugin_proxies.emplace(proxy_object.instance_id(),
std::ref<Vst3PluginProxyImpl>(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());
}
}
+20 -10
View File
@@ -76,13 +76,12 @@ class Vst3PluginBridge : PluginBridge<Vst3Sockets<std::jthread>> {
/**
* 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<Vst3Sockets<std::jthread>> {
* 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<Vst3Sockets<std::jthread>> {
object, std::pair<Vst3Logger&, bool>(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>
typename T::Response send_audio_processor_message(const T& object) {
return sockets.send_audio_processor_message(
object, std::pair<Vst3Logger&, bool>(logger, true));
}
/**
* The logging facility used for this instance of yabridge. Wraps around
* `PluginBridge::generic_logger`.
+202 -120
View File
@@ -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<void> 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
+7
View File
@@ -38,6 +38,13 @@ struct InstanceInterfaces {
InstanceInterfaces(Steinberg::IPtr<Steinberg::FUnknown> 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